python-coverage-4.5+dfsg.1.orig/0000755000076600000620000000000013235414516014462 5ustar staffpython-coverage-4.5+dfsg.1.orig/metacov.ini0000644000076600000620000000264013173515667016635 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Settings to use when using coverage.py to measure itself. [run] branch = true data_file = $COVERAGE_METAFILE parallel = true source = $COVERAGE_HOME/coverage $COVERAGE_HOME/tests [report] # We set a different pragma so our code won't be confused with test code. exclude_lines = pragma: not covered # Lines in test code that isn't covered: we are nested inside ourselves. pragma: nested # Lines that are only executed when we are debugging coverage.py. def __repr__ pragma: debugging # Lines that are only executed when we are not testing coverage.py. pragma: not testing # Lines that we can't run during metacov. pragma: no metacov # These lines only happen if tests fail. raise AssertionError pragma: only failure # OS error conditions that we can't (or don't care to) replicate. pragma: cant happen # Jython needs special care. pragma: only jython skip.*Jython # IronPython isn't included in metacoverage. pragma: only ironpython partial_branches = pragma: part covered pragma: if failure if env.TESTING: if .* env.JYTHON if .* env.IRONPYTHON ignore_errors = true precision = 1 [paths] source = . *\coverage\trunk */coverage/trunk *\coveragepy python-coverage-4.5+dfsg.1.orig/.travis.yml0000644000076600000620000000106413177624110016572 0ustar staff# Tell Travis what to do # https://travis-ci.org/nedbat/coveragepy language: python sudo: false python: - '2.6' - '2.7' - '3.3' - '3.4' - '3.5' - '3.6' - 'pypy' env: matrix: - COVERAGE_COVERAGE=no - COVERAGE_COVERAGE=yes sudo: false install: - pip install -r requirements/ci.pip script: - tox - if [[ $COVERAGE_COVERAGE == 'yes' ]]; then python igor.py combine_html; fi - if [[ $COVERAGE_COVERAGE == 'yes' ]]; then pip install codecov; fi - if [[ $COVERAGE_COVERAGE == 'yes' ]]; then codecov -X gcov --file coverage.xml; fi python-coverage-4.5+dfsg.1.orig/setup.cfg0000644000076600000620000000042713235414516016306 0ustar staff[tool:pytest] addopts = -q -n3 --strict --no-flaky-report markers = expensive: too slow to run during "make smoke" [pep8] ignore = E265,E266,E123,E133,E226,E241,E242,E301,E401 max-line-length = 100 [metadata] license_file = LICENSE.txt [egg_info] tag_build = tag_date = 0 python-coverage-4.5+dfsg.1.orig/CHANGES.rst0000644000076600000620000027356513235412455016306 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt ============================== Change history for Coverage.py ============================== Version 4.5 --- 2018-02-03 -------------------------- - A new kind of plugin is supported: configurators are invoked at start-up to allow more complex configuration than the .coveragerc file can easily do. See :ref:`api_plugin` for details. This solves the complex configuration problem described in `issue 563`_. - The ``fail_under`` option can now be a float. Note that you must specify the ``[report] precision`` configuration option for the fractional part to be used. Thanks to Lars Hupfeldt Nielsen for help with the implementation. Fixes `issue 631`_. - The ``include`` and ``omit`` options can be specified for both the ``[run]`` and ``[report]`` phases of execution. 4.4.2 introduced some incorrect interactions between those phases, where the options for one were confused for the other. This is now corrected, fixing `issue 621`_ and `issue 622`_. Thanks to Daniel Hahler for seeing more clearly than I could. - The ``coverage combine`` command used to always overwrite the data file, even when no data had been read from apparently combinable files. Now, an error is raised if we thought there were files to combine, but in fact none of them could be used. Fixes `issue 629`_. - The ``coverage combine`` command could get confused about path separators when combining data collected on Windows with data collected on Linux, as described in `issue 618`_. This is now fixed: the result path always uses the path separator specified in the ``[paths]`` result. - On Windows, the HTML report could fail when source trees are deeply nested, due to attempting to create HTML filenames longer than the 250-character maximum. Now filenames will never get much larger than 200 characters, fixing `issue 627`_. Thanks to Alex Sandro for helping with the fix. .. _issue 563: https://bitbucket.org/ned/coveragepy/issues/563/platform-specific-configuration .. _issue 618: https://bitbucket.org/ned/coveragepy/issues/618/problem-when-combining-windows-generated .. _issue 621: https://bitbucket.org/ned/coveragepy/issues/621/include-ignored-warning-when-using .. _issue 622: https://bitbucket.org/ned/coveragepy/issues/622/report-omit-overwrites-run-omit .. _issue 627: https://bitbucket.org/ned/coveragepy/issues/627/failure-generating-html-reports-when-the .. _issue 629: https://bitbucket.org/ned/coveragepy/issues/629/multiple-use-of-combine-leads-to-empty .. _issue 631: https://bitbucket.org/ned/coveragepy/issues/631/precise-coverage-percentage-value .. _changes_442: Version 4.4.2 --- 2017-11-05 ---------------------------- - Support for Python 3.7. In some cases, class and module docstrings are no longer counted in statement totals, which could slightly change your total results. - Specifying both ``--source`` and ``--include`` no longer silently ignores the include setting, instead it displays a warning. Thanks, Loïc Dachary. Closes `issue 265`_ and `issue 101`_. - Fixed a race condition when saving data and multiple threads are tracing (`issue 581`_). It could produce a "dictionary changed size during iteration" RuntimeError. I believe this mostly but not entirely fixes the race condition. A true fix would likely be too expensive. Thanks, Peter Baughman for the debugging, and Olivier Grisel for the fix with tests. - Configuration values which are file paths will now apply tilde-expansion, closing `issue 589`_. - Now secondary config files like tox.ini and setup.cfg can be specified explicitly, and prefixed sections like `[coverage:run]` will be read. Fixes `issue 588`_. - Be more flexible about the command name displayed by help, fixing `issue 600`_. Thanks, Ben Finney. .. _issue 101: https://bitbucket.org/ned/coveragepy/issues/101/settings-under-report-affect-running .. _issue 581: https://bitbucket.org/ned/coveragepy/issues/581/race-condition-when-saving-data-under .. _issue 588: https://bitbucket.org/ned/coveragepy/issues/588/using-rcfile-path-to-toxini-uses-run .. _issue 589: https://bitbucket.org/ned/coveragepy/issues/589/allow-expansion-in-coveragerc .. _issue 600: https://bitbucket.org/ned/coveragepy/issues/600/get-program-name-from-command-line-when .. _changes_441: Version 4.4.1 --- 2017-05-14 ---------------------------- - No code changes: just corrected packaging for Python 2.7 Linux wheels. .. _changes_44: Version 4.4 --- 2017-05-07 -------------------------- - Reports could produce the wrong file names for packages, reporting ``pkg.py`` instead of the correct ``pkg/__init__.py``. This is now fixed. Thanks, Dirk Thomas. - XML reports could produce ```` and ```` lines that together didn't specify a valid source file path. This is now fixed. (`issue 526`_) - Namespace packages are no longer warned as having no code. (`issue 572`_) - Code that uses ``sys.settrace(sys.gettrace())`` in a file that wasn't being coverage-measured would prevent correct coverage measurement in following code. An example of this was running doctests programmatically. This is now fixed. (`issue 575`_) - Errors printed by the ``coverage`` command now go to stderr instead of stdout. - Running ``coverage xml`` in a directory named with non-ASCII characters would fail under Python 2. This is now fixed. (`issue 573`_) .. _issue 526: https://bitbucket.org/ned/coveragepy/issues/526/generated-xml-invalid-paths-for-cobertura .. _issue 572: https://bitbucket.org/ned/coveragepy/issues/572/no-python-source-warning-for-namespace .. _issue 573: https://bitbucket.org/ned/coveragepy/issues/573/cant-generate-xml-report-if-some-source .. _issue 575: https://bitbucket.org/ned/coveragepy/issues/575/running-doctest-prevents-complete-coverage Version 4.4b1 --- 2017-04-04 ---------------------------- - Some warnings can now be individually disabled. Warnings that can be disabled have a short name appended. The ``[run] disable_warnings`` setting takes a list of these warning names to disable. Closes both `issue 96`_ and `issue 355`_. - The XML report now includes attributes from version 4 of the Cobertura XML format, fixing `issue 570`_. - In previous versions, calling a method that used collected data would prevent further collection. For example, `save()`, `report()`, `html_report()`, and others would all stop collection. An explicit `start()` was needed to get it going again. This is no longer true. Now you can use the collected data and also continue measurement. Both `issue 79`_ and `issue 448`_ described this problem, and have been fixed. - Plugins can now find unexecuted files if they choose, by implementing the `find_executable_files` method. Thanks, Emil Madsen. - Minimal IronPython support. You should be able to run IronPython programs under ``coverage run``, though you will still have to do the reporting phase with CPython. - Coverage.py has long had a special hack to support CPython's need to measure the coverage of the standard library tests. This code was not installed by kitted versions of coverage.py. Now it is. .. _issue 79: https://bitbucket.org/ned/coveragepy/issues/79/save-prevents-harvesting-on-stop .. _issue 96: https://bitbucket.org/ned/coveragepy/issues/96/unhelpful-warnings-produced-when-using .. _issue 355: https://bitbucket.org/ned/coveragepy/issues/355/warnings-should-be-suppressable .. _issue 448: https://bitbucket.org/ned/coveragepy/issues/448/save-and-html_report-prevent-further .. _issue 570: https://bitbucket.org/ned/coveragepy/issues/570/cobertura-coverage-04dtd-support .. _changes_434: Version 4.3.4 --- 2017-01-17 ---------------------------- - Fixing 2.6 in version 4.3.3 broke other things, because the too-tricky exception wasn't properly derived from Exception, described in `issue 556`_. A newb mistake; it hasn't been a good few days. .. _issue 556: https://bitbucket.org/ned/coveragepy/issues/556/43-fails-if-there-are-html-files-in-the .. _changes_433: Version 4.3.3 --- 2017-01-17 ---------------------------- - Python 2.6 support was broken due to a testing exception imported for the benefit of the coverage.py test suite. Properly conditionalizing it fixed `issue 554`_ so that Python 2.6 works again. .. _issue 554: https://bitbucket.org/ned/coveragepy/issues/554/traceback-on-python-26-starting-with-432 .. _changes_432: Version 4.3.2 --- 2017-01-16 ---------------------------- - Using the ``--skip-covered`` option on an HTML report with 100% coverage would cause a "No data to report" error, as reported in `issue 549`_. This is now fixed; thanks, Loïc Dachary. - If-statements can be optimized away during compilation, for example, `if 0:` or `if __debug__:`. Coverage.py had problems properly understanding these statements which existed in the source, but not in the compiled bytecode. This problem, reported in `issue 522`_, is now fixed. - If you specified ``--source`` as a directory, then coverage.py would look for importable Python files in that directory, and could identify ones that had never been executed at all. But if you specified it as a package name, that detection wasn't performed. Now it is, closing `issue 426`_. Thanks to Loïc Dachary for the fix. - If you started and stopped coverage measurement thousands of times in your process, you could crash Python with a "Fatal Python error: deallocating None" error. This is now fixed. Thanks to Alex Groce for the bug report. - On PyPy, measuring coverage in subprocesses could produce a warning: "Trace function changed, measurement is likely wrong: None". This was spurious, and has been suppressed. - Previously, coverage.py couldn't start on Jython, due to that implementation missing the multiprocessing module (`issue 551`_). This problem has now been fixed. Also, `issue 322`_ about not being able to invoke coverage conveniently, seems much better: ``jython -m coverage run myprog.py`` works properly. - Let's say you ran the HTML report over and over again in the same output directory, with ``--skip-covered``. And imagine due to your heroic test-writing efforts, a file just achieved the goal of 100% coverage. With coverage.py 4.3, the old HTML file with the less-than-100% coverage would be left behind. This file is now properly deleted. .. _issue 322: https://bitbucket.org/ned/coveragepy/issues/322/cannot-use-coverage-with-jython .. _issue 426: https://bitbucket.org/ned/coveragepy/issues/426/difference-between-coverage-results-with .. _issue 522: https://bitbucket.org/ned/coveragepy/issues/522/incorrect-branch-reporting-with-__debug__ .. _issue 549: https://bitbucket.org/ned/coveragepy/issues/549/skip-covered-with-100-coverage-throws-a-no .. _issue 551: https://bitbucket.org/ned/coveragepy/issues/551/coveragepy-cannot-be-imported-in-jython27 .. _changes_431: Version 4.3.1 --- 2016-12-28 ---------------------------- - Some environments couldn't install 4.3, as described in `issue 540`_. This is now fixed. - The check for conflicting ``--source`` and ``--include`` was too simple in a few different ways, breaking a few perfectly reasonable use cases, described in `issue 541`_. The check has been reverted while we re-think the fix for `issue 265`_. .. _issue 540: https://bitbucket.org/ned/coveragepy/issues/540/cant-install-coverage-v43-into-under .. _issue 541: https://bitbucket.org/ned/coveragepy/issues/541/coverage-43-breaks-nosetest-with-coverage .. _changes_43: Version 4.3 --- 2016-12-27 -------------------------- Special thanks to **Loïc Dachary**, who took an extraordinary interest in coverage.py and contributed a number of improvements in this release. - Subprocesses that are measured with `automatic subprocess measurement`_ used to read in any pre-existing data file. This meant data would be incorrectly carried forward from run to run. Now those files are not read, so each subprocess only writes its own data. Fixes `issue 510`_. - The ``coverage combine`` command will now fail if there are no data files to combine. The combine changes in 4.2 meant that multiple combines could lose data, leaving you with an empty .coverage data file. Fixes `issue 525`_, `issue 412`_, `issue 516`_, and probably `issue 511`_. - Coverage.py wouldn't execute `sys.excepthook`_ when an exception happened in your program. Now it does, thanks to Andrew Hoos. Closes `issue 535`_. - Branch coverage fixes: - Branch coverage could misunderstand a finally clause on a try block that never continued on to the following statement, as described in `issue 493`_. This is now fixed. Thanks to Joe Doherty for the report and Loïc Dachary for the fix. - A while loop with a constant condition (while True) and a continue statement would be mis-analyzed, as described in `issue 496`_. This is now fixed, thanks to a bug report by Eli Skeggs and a fix by Loïc Dachary. - While loops with constant conditions that were never executed could result in a non-zero coverage report. Artem Dayneko reported this in `issue 502`_, and Loïc Dachary provided the fix. - The HTML report now supports a ``--skip-covered`` option like the other reporting commands. Thanks, Loïc Dachary for the implementation, closing `issue 433`_. - Options can now be read from a tox.ini file, if any. Like setup.cfg, sections are prefixed with "coverage:", so ``[run]`` options will be read from the ``[coverage:run]`` section of tox.ini. Implements part of `issue 519`_. Thanks, Stephen Finucane. - Specifying both ``--source`` and ``--include`` no longer silently ignores the include setting, instead it fails with a message. Thanks, Nathan Land and Loïc Dachary. Closes `issue 265`_. - The ``Coverage.combine`` method has a new parameter, ``strict=False``, to support failing if there are no data files to combine. - When forking subprocesses, the coverage data files would have the same random number appended to the file name. This didn't cause problems, because the file names had the process id also, making collisions (nearly) impossible. But it was disconcerting. This is now fixed. - The text report now properly sizes headers when skipping some files, fixing `issue 524`_. Thanks, Anthony Sottile and Loïc Dachary. - Coverage.py can now search .pex files for source, just as it can .zip and .egg. Thanks, Peter Ebden. - Data files are now about 15% smaller. - Improvements in the ``[run] debug`` setting: - The "dataio" debug setting now also logs when data files are deleted during combining or erasing. - A new debug option, "multiproc", for logging the behavior of ``concurrency=multiprocessing``. - If you used the debug options "config" and "callers" together, you'd get a call stack printed for every line in the multi-line config output. This is now fixed. - Fixed an unusual bug involving multiple coding declarations affecting code containing code in multi-line strings: `issue 529`_. - Coverage.py will no longer be misled into thinking that a plain file is a package when interpreting ``--source`` options. Thanks, Cosimo Lupo. - If you try to run a non-Python file with coverage.py, you will now get a more useful error message. `Issue 514`_. - The default pragma regex changed slightly, but this will only matter to you if you are deranged and use mixed-case pragmas. - Deal properly with non-ASCII file names in an ASCII-only world, `issue 533`_. - Programs that set Unicode configuration values could cause UnicodeErrors when generating HTML reports. Pytest-cov is one example. This is now fixed. - Prevented deprecation warnings from configparser that happened in some circumstances, closing `issue 530`_. - Corrected the name of the jquery.ba-throttle-debounce.js library. Thanks, Ben Finney. Closes `issue 505`_. - Testing against PyPy 5.6 and PyPy3 5.5. - Switched to pytest from nose for running the coverage.py tests. - Renamed AUTHORS.txt to CONTRIBUTORS.txt, since there are other ways to contribute than by writing code. Also put the count of contributors into the author string in setup.py, though this might be too cute. .. _sys.excepthook: https://docs.python.org/3/library/sys.html#sys.excepthook .. _issue 265: https://bitbucket.org/ned/coveragepy/issues/265/when-using-source-include-is-silently .. _issue 412: https://bitbucket.org/ned/coveragepy/issues/412/coverage-combine-should-error-if-no .. _issue 433: https://bitbucket.org/ned/coveragepy/issues/433/coverage-html-does-not-suport-skip-covered .. _issue 493: https://bitbucket.org/ned/coveragepy/issues/493/confusing-branching-failure .. _issue 496: https://bitbucket.org/ned/coveragepy/issues/496/incorrect-coverage-with-branching-and .. _issue 502: https://bitbucket.org/ned/coveragepy/issues/502/incorrect-coverage-report-with-cover .. _issue 505: https://bitbucket.org/ned/coveragepy/issues/505/use-canonical-filename-for-debounce .. _issue 514: https://bitbucket.org/ned/coveragepy/issues/514/path-to-problem-file-not-reported-when .. _issue 510: https://bitbucket.org/ned/coveragepy/issues/510/erase-still-needed-in-42 .. _issue 511: https://bitbucket.org/ned/coveragepy/issues/511/version-42-coverage-combine-empties .. _issue 516: https://bitbucket.org/ned/coveragepy/issues/516/running-coverage-combine-twice-deletes-all .. _issue 519: https://bitbucket.org/ned/coveragepy/issues/519/coverage-run-sections-in-toxini-or-as .. _issue 524: https://bitbucket.org/ned/coveragepy/issues/524/coverage-report-with-skip-covered-column .. _issue 525: https://bitbucket.org/ned/coveragepy/issues/525/coverage-combine-when-not-in-parallel-mode .. _issue 529: https://bitbucket.org/ned/coveragepy/issues/529/encoding-marker-may-only-appear-on-the .. _issue 530: https://bitbucket.org/ned/coveragepy/issues/530/deprecationwarning-you-passed-a-bytestring .. _issue 533: https://bitbucket.org/ned/coveragepy/issues/533/exception-on-unencodable-file-name .. _issue 535: https://bitbucket.org/ned/coveragepy/issues/535/sysexcepthook-is-not-called .. _changes_42: Version 4.2 --- 2016-07-26 -------------------------- - Since ``concurrency=multiprocessing`` uses subprocesses, options specified on the coverage.py command line will not be communicated down to them. Only options in the configuration file will apply to the subprocesses. Previously, the options didn't apply to the subprocesses, but there was no indication. Now it is an error to use ``--concurrency=multiprocessing`` and other run-affecting options on the command line. This prevents failures like those reported in `issue 495`_. - Filtering the HTML report is now faster, thanks to Ville Skyttä. .. _issue 495: https://bitbucket.org/ned/coveragepy/issues/495/branch-and-concurrency-are-conflicting Version 4.2b1 --- 2016-07-04 ---------------------------- Work from the PyCon 2016 Sprints! - BACKWARD INCOMPATIBILITY: the ``coverage combine`` command now ignores an existing ``.coverage`` data file. It used to include that file in its combining. This caused confusing results, and extra tox "clean" steps. If you want the old behavior, use the new ``coverage combine --append`` option. - The ``concurrency`` option can now take multiple values, to support programs using multiprocessing and another library such as eventlet. This is only possible in the configuration file, not from the command line. The configuration file is the only way for sub-processes to all run with the same options. Fixes `issue 484`_. Thanks to Josh Williams for prototyping. - Using a ``concurrency`` setting of ``multiprocessing`` now implies ``--parallel`` so that the main program is measured similarly to the sub-processes. - When using `automatic subprocess measurement`_, running coverage commands would create spurious data files. This is now fixed, thanks to diagnosis and testing by Dan Riti. Closes `issue 492`_. - A new configuration option, ``report:sort``, controls what column of the text report is used to sort the rows. Thanks to Dan Wandschneider, this closes `issue 199`_. - The HTML report has a more-visible indicator for which column is being sorted. Closes `issue 298`_, thanks to Josh Williams. - If the HTML report cannot find the source for a file, the message now suggests using the ``-i`` flag to allow the report to continue. Closes `issue 231`_, thanks, Nathan Land. - When reports are ignoring errors, there's now a warning if a file cannot be parsed, rather than being silently ignored. Closes `issue 396`_. Thanks, Matthew Boehm. - A new option for ``coverage debug`` is available: ``coverage debug config`` shows the current configuration. Closes `issue 454`_, thanks to Matthew Boehm. - Running coverage as a module (``python -m coverage``) no longer shows the program name as ``__main__.py``. Fixes `issue 478`_. Thanks, Scott Belden. - The `test_helpers` module has been moved into a separate pip-installable package: `unittest-mixins`_. .. _automatic subprocess measurement: https://coverage.readthedocs.io/en/latest/subprocess.html .. _issue 199: https://bitbucket.org/ned/coveragepy/issues/199/add-a-way-to-sort-the-text-report .. _issue 231: https://bitbucket.org/ned/coveragepy/issues/231/various-default-behavior-in-report-phase .. _issue 298: https://bitbucket.org/ned/coveragepy/issues/298/show-in-html-report-that-the-columns-are .. _issue 396: https://bitbucket.org/ned/coveragepy/issues/396/coverage-xml-shouldnt-bail-out-on-parse .. _issue 454: https://bitbucket.org/ned/coveragepy/issues/454/coverage-debug-config-should-be .. _issue 478: https://bitbucket.org/ned/coveragepy/issues/478/help-shows-silly-program-name-when-running .. _issue 484: https://bitbucket.org/ned/coveragepy/issues/484/multiprocessing-greenlet-concurrency .. _issue 492: https://bitbucket.org/ned/coveragepy/issues/492/subprocess-coverage-strange-detection-of .. _unittest-mixins: https://pypi.python.org/pypi/unittest-mixins .. _changes_41: Version 4.1 --- 2016-05-21 -------------------------- - The internal attribute `Reporter.file_reporters` was removed in 4.1b3. It should have come has no surprise that there were third-party tools out there using that attribute. It has been restored, but with a deprecation warning. Version 4.1b3 --- 2016-05-10 ---------------------------- - When running your program, execution can jump from an ``except X:`` line to some other line when an exception other than ``X`` happens. This jump is no longer considered a branch when measuring branch coverage. - When measuring branch coverage, ``yield`` statements that were never resumed were incorrectly marked as missing, as reported in `issue 440`_. This is now fixed. - During branch coverage of single-line callables like lambdas and generator expressions, coverage.py can now distinguish between them never being called, or being called but not completed. Fixes `issue 90`_, `issue 460`_ and `issue 475`_. - The HTML report now has a map of the file along the rightmost edge of the page, giving an overview of where the missed lines are. Thanks, Dmitry Shishov. - The HTML report now uses different monospaced fonts, favoring Consolas over Courier. Along the way, `issue 472`_ about not properly handling one-space indents was fixed. The index page also has slightly different styling, to try to make the clickable detail pages more apparent. - Missing branches reported with ``coverage report -m`` will now say ``->exit`` for missed branches to the exit of a function, rather than a negative number. Fixes `issue 469`_. - ``coverage --help`` and ``coverage --version`` now mention which tracer is installed, to help diagnose problems. The docs mention which features need the C extension. (`issue 479`_) - Officially support PyPy 5.1, which required no changes, just updates to the docs. - The `Coverage.report` function had two parameters with non-None defaults, which have been changed. `show_missing` used to default to True, but now defaults to None. If you had been calling `Coverage.report` without specifying `show_missing`, you'll need to explicitly set it to True to keep the same behavior. `skip_covered` used to default to False. It is now None, which doesn't change the behavior. This fixes `issue 485`_. - It's never been possible to pass a namespace module to one of the analysis functions, but now at least we raise a more specific error message, rather than getting confused. (`issue 456`_) - The `coverage.process_startup` function now returns the `Coverage` instance it creates, as suggested in `issue 481`_. - Make a small tweak to how we compare threads, to avoid buggy custom comparison code in thread classes. (`issue 245`_) .. _issue 90: https://bitbucket.org/ned/coveragepy/issues/90/lambda-expression-confuses-branch .. _issue 245: https://bitbucket.org/ned/coveragepy/issues/245/change-solution-for-issue-164 .. _issue 440: https://bitbucket.org/ned/coveragepy/issues/440/yielded-twisted-failure-marked-as-missed .. _issue 456: https://bitbucket.org/ned/coveragepy/issues/456/coverage-breaks-with-implicit-namespaces .. _issue 460: https://bitbucket.org/ned/coveragepy/issues/460/confusing-html-report-for-certain-partial .. _issue 469: https://bitbucket.org/ned/coveragepy/issues/469/strange-1-line-number-in-branch-coverage .. _issue 472: https://bitbucket.org/ned/coveragepy/issues/472/html-report-indents-incorrectly-for-one .. _issue 475: https://bitbucket.org/ned/coveragepy/issues/475/generator-expression-is-marked-as-not .. _issue 479: https://bitbucket.org/ned/coveragepy/issues/479/clarify-the-need-for-the-c-extension .. _issue 481: https://bitbucket.org/ned/coveragepy/issues/481/asyncioprocesspoolexecutor-tracing-not .. _issue 485: https://bitbucket.org/ned/coveragepy/issues/485/coveragereport-ignores-show_missing-and Version 4.1b2 --- 2016-01-23 ---------------------------- - Problems with the new branch measurement in 4.1 beta 1 were fixed: - Class docstrings were considered executable. Now they no longer are. - ``yield from`` and ``await`` were considered returns from functions, since they could tranfer control to the caller. This produced unhelpful "missing branch" reports in a number of circumstances. Now they no longer are considered returns. - In unusual situations, a missing branch to a negative number was reported. This has been fixed, closing `issue 466`_. - The XML report now produces correct package names for modules found in directories specified with ``source=``. Fixes `issue 465`_. - ``coverage report`` won't produce trailing whitespace. .. _issue 465: https://bitbucket.org/ned/coveragepy/issues/465/coveragexml-produces-package-names-with-an .. _issue 466: https://bitbucket.org/ned/coveragepy/issues/466/impossible-missed-branch-to-a-negative Version 4.1b1 --- 2016-01-10 ---------------------------- - Branch analysis has been rewritten: it used to be based on bytecode, but now uses AST analysis. This has changed a number of things: - More code paths are now considered runnable, especially in ``try``/``except`` structures. This may mean that coverage.py will identify more code paths as uncovered. This could either raise or lower your overall coverage number. - Python 3.5's ``async`` and ``await`` keywords are properly supported, fixing `issue 434`_. - Some long-standing branch coverage bugs were fixed: - `issue 129`_: functions with only a docstring for a body would incorrectly report a missing branch on the ``def`` line. - `issue 212`_: code in an ``except`` block could be incorrectly marked as a missing branch. - `issue 146`_: context managers (``with`` statements) in a loop or ``try`` block could confuse the branch measurement, reporting incorrect partial branches. - `issue 422`_: in Python 3.5, an actual partial branch could be marked as complete. - Pragmas to disable coverage measurement can now be used on decorator lines, and they will apply to the entire function or class being decorated. This implements the feature requested in `issue 131`_. - Multiprocessing support is now available on Windows. Thanks, Rodrigue Cloutier. - Files with two encoding declarations are properly supported, fixing `issue 453`_. Thanks, Max Linke. - Non-ascii characters in regexes in the configuration file worked in 3.7, but stopped working in 4.0. Now they work again, closing `issue 455`_. - Form-feed characters would prevent accurate determination of the beginning of statements in the rest of the file. This is now fixed, closing `issue 461`_. .. _issue 129: https://bitbucket.org/ned/coveragepy/issues/129/misleading-branch-coverage-of-empty .. _issue 131: https://bitbucket.org/ned/coveragepy/issues/131/pragma-on-a-decorator-line-should-affect .. _issue 146: https://bitbucket.org/ned/coveragepy/issues/146/context-managers-confuse-branch-coverage .. _issue 212: https://bitbucket.org/ned/coveragepy/issues/212/coverage-erroneously-reports-partial .. _issue 422: https://bitbucket.org/ned/coveragepy/issues/422/python35-partial-branch-marked-as-fully .. _issue 434: https://bitbucket.org/ned/coveragepy/issues/434/indexerror-in-python-35 .. _issue 453: https://bitbucket.org/ned/coveragepy/issues/453/source-code-encoding-can-only-be-specified .. _issue 455: https://bitbucket.org/ned/coveragepy/issues/455/unusual-exclusions-stopped-working-in .. _issue 461: https://bitbucket.org/ned/coveragepy/issues/461/multiline-asserts-need-too-many-pragma .. _changes_403: Version 4.0.3 --- 2015-11-24 ---------------------------- - Fixed a mysterious problem that manifested in different ways: sometimes hanging the process (`issue 420`_), sometimes making database connections fail (`issue 445`_). - The XML report now has correct ```` elements when using a ``--source=`` option somewhere besides the current directory. This fixes `issue 439`_. Thanks, Arcady Ivanov. - Fixed an unusual edge case of detecting source encodings, described in `issue 443`_. - Help messages that mention the command to use now properly use the actual command name, which might be different than "coverage". Thanks to Ben Finney, this closes `issue 438`_. .. _issue 420: https://bitbucket.org/ned/coveragepy/issues/420/coverage-40-hangs-indefinitely-on-python27 .. _issue 438: https://bitbucket.org/ned/coveragepy/issues/438/parameterise-coverage-command-name .. _issue 439: https://bitbucket.org/ned/coveragepy/issues/439/incorrect-cobertura-file-sources-generated .. _issue 443: https://bitbucket.org/ned/coveragepy/issues/443/coverage-gets-confused-when-encoding .. _issue 445: https://bitbucket.org/ned/coveragepy/issues/445/django-app-cannot-connect-to-cassandra .. _changes_402: Version 4.0.2 --- 2015-11-04 ---------------------------- - More work on supporting unusually encoded source. Fixed `issue 431`_. - Files or directories with non-ASCII characters are now handled properly, fixing `issue 432`_. - Setting a trace function with sys.settrace was broken by a change in 4.0.1, as reported in `issue 436`_. This is now fixed. - Officially support PyPy 4.0, which required no changes, just updates to the docs. .. _issue 431: https://bitbucket.org/ned/coveragepy/issues/431/couldnt-parse-python-file-with-cp1252 .. _issue 432: https://bitbucket.org/ned/coveragepy/issues/432/path-with-unicode-characters-various .. _issue 436: https://bitbucket.org/ned/coveragepy/issues/436/disabled-coverage-ctracer-may-rise-from .. _changes_401: Version 4.0.1 --- 2015-10-13 ---------------------------- - When combining data files, unreadable files will now generate a warning instead of failing the command. This is more in line with the older coverage.py v3.7.1 behavior, which silently ignored unreadable files. Prompted by `issue 418`_. - The --skip-covered option would skip reporting on 100% covered files, but also skipped them when calculating total coverage. This was wrong, it should only remove lines from the report, not change the final answer. This is now fixed, closing `issue 423`_. - In 4.0, the data file recorded a summary of the system on which it was run. Combined data files would keep all of those summaries. This could lead to enormous data files consisting of mostly repetitive useless information. That summary is now gone, fixing `issue 415`_. If you want summary information, get in touch, and we'll figure out a better way to do it. - Test suites that mocked os.path.exists would experience strange failures, due to coverage.py using their mock inadvertently. This is now fixed, closing `issue 416`_. - Importing a ``__init__`` module explicitly would lead to an error: ``AttributeError: 'module' object has no attribute '__path__'``, as reported in `issue 410`_. This is now fixed. - Code that uses ``sys.settrace(sys.gettrace())`` used to incur a more than 2x speed penalty. Now there's no penalty at all. Fixes `issue 397`_. - Pyexpat C code will no longer be recorded as a source file, fixing `issue 419`_. - The source kit now contains all of the files needed to have a complete source tree, re-fixing `issue 137`_ and closing `issue 281`_. .. _issue 281: https://bitbucket.org/ned/coveragepy/issues/281/supply-scripts-for-testing-in-the .. _issue 397: https://bitbucket.org/ned/coveragepy/issues/397/stopping-and-resuming-coverage-with .. _issue 410: https://bitbucket.org/ned/coveragepy/issues/410/attributeerror-module-object-has-no .. _issue 415: https://bitbucket.org/ned/coveragepy/issues/415/repeated-coveragedataupdates-cause .. _issue 416: https://bitbucket.org/ned/coveragepy/issues/416/mocking-ospathexists-causes-failures .. _issue 418: https://bitbucket.org/ned/coveragepy/issues/418/json-parse-error .. _issue 419: https://bitbucket.org/ned/coveragepy/issues/419/nosource-no-source-for-code-path-to-c .. _issue 423: https://bitbucket.org/ned/coveragepy/issues/423/skip_covered-changes-reported-total .. _changes_40: Version 4.0 --- 2015-09-20 -------------------------- No changes from 4.0b3 Version 4.0b3 --- 2015-09-07 ---------------------------- - Reporting on an unmeasured file would fail with a traceback. This is now fixed, closing `issue 403`_. - The Jenkins ShiningPanda plugin looks for an obsolete file name to find the HTML reports to publish, so it was failing under coverage.py 4.0. Now we create that file if we are running under Jenkins, to keep things working smoothly. `issue 404`_. - Kits used to include tests and docs, but didn't install them anywhere, or provide all of the supporting tools to make them useful. Kits no longer include tests and docs. If you were using them from the older packages, get in touch and help me understand how. .. _issue 403: https://bitbucket.org/ned/coveragepy/issues/403/hasherupdate-fails-with-typeerror-nonetype .. _issue 404: https://bitbucket.org/ned/coveragepy/issues/404/shiningpanda-jenkins-plugin-cant-find-html Version 4.0b2 --- 2015-08-22 ---------------------------- - 4.0b1 broke ``--append`` creating new data files. This is now fixed, closing `issue 392`_. - ``py.test --cov`` can write empty data, then touch files due to ``--source``, which made coverage.py mistakenly force the data file to record lines instead of arcs. This would lead to a "Can't combine line data with arc data" error message. This is now fixed, and changed some method names in the CoverageData interface. Fixes `issue 399`_. - `CoverageData.read_fileobj` and `CoverageData.write_fileobj` replace the `.read` and `.write` methods, and are now properly inverses of each other. - When using ``report --skip-covered``, a message will now be included in the report output indicating how many files were skipped, and if all files are skipped, coverage.py won't accidentally scold you for having no data to report. Thanks, Krystian Kichewko. - A new conversion utility has been added: ``python -m coverage.pickle2json`` will convert v3.x pickle data files to v4.x JSON data files. Thanks, Alexander Todorov. Closes `issue 395`_. - A new version identifier is available, `coverage.version_info`, a plain tuple of values similar to `sys.version_info`_. .. _issue 392: https://bitbucket.org/ned/coveragepy/issues/392/run-append-doesnt-create-coverage-file .. _issue 395: https://bitbucket.org/ned/coveragepy/issues/395/rfe-read-pickled-files-as-well-for .. _issue 399: https://bitbucket.org/ned/coveragepy/issues/399/coverageexception-cant-combine-line-data .. _sys.version_info: https://docs.python.org/3/library/sys.html#sys.version_info Version 4.0b1 --- 2015-08-02 ---------------------------- - Coverage.py is now licensed under the Apache 2.0 license. See NOTICE.txt for details. Closes `issue 313`_. - The data storage has been completely revamped. The data file is now JSON-based instead of a pickle, closing `issue 236`_. The `CoverageData` class is now a public supported documented API to the data file. - A new configuration option, ``[run] note``, lets you set a note that will be stored in the `runs` section of the data file. You can use this to annotate the data file with any information you like. - Unrecognized configuration options will now print an error message and stop coverage.py. This should help prevent configuration mistakes from passing silently. Finishes `issue 386`_. - In parallel mode, ``coverage erase`` will now delete all of the data files, fixing `issue 262`_. - Coverage.py now accepts a directory name for ``coverage run`` and will run a ``__main__.py`` found there, just like Python will. Fixes `issue 252`_. Thanks, Dmitry Trofimov. - The XML report now includes a ``missing-branches`` attribute. Thanks, Steve Peak. This is not a part of the Cobertura DTD, so the XML report no longer references the DTD. - Missing branches in the HTML report now have a bit more information in the right-hand annotations. Hopefully this will make their meaning clearer. - All the reporting functions now behave the same if no data had been collected, exiting with a status code of 1. Fixed ``fail_under`` to be applied even when the report is empty. Thanks, Ionel Cristian Mărieș. - Plugins are now initialized differently. Instead of looking for a class called ``Plugin``, coverage.py looks for a function called ``coverage_init``. - A file-tracing plugin can now ask to have built-in Python reporting by returning `"python"` from its `file_reporter()` method. - Code that was executed with `exec` would be mis-attributed to the file that called it. This is now fixed, closing `issue 380`_. - The ability to use item access on `Coverage.config` (introduced in 4.0a2) has been changed to a more explicit `Coverage.get_option` and `Coverage.set_option` API. - The ``Coverage.use_cache`` method is no longer supported. - The private method ``Coverage._harvest_data`` is now called ``Coverage.get_data``, and returns the ``CoverageData`` containing the collected data. - The project is consistently referred to as "coverage.py" throughout the code and the documentation, closing `issue 275`_. - Combining data files with an explicit configuration file was broken in 4.0a6, but now works again, closing `issue 385`_. - ``coverage combine`` now accepts files as well as directories. - The speed is back to 3.7.1 levels, after having slowed down due to plugin support, finishing up `issue 387`_. .. _issue 236: https://bitbucket.org/ned/coveragepy/issues/236/pickles-are-bad-and-you-should-feel-bad .. _issue 252: https://bitbucket.org/ned/coveragepy/issues/252/coverage-wont-run-a-program-with .. _issue 262: https://bitbucket.org/ned/coveragepy/issues/262/when-parallel-true-erase-should-erase-all .. _issue 275: https://bitbucket.org/ned/coveragepy/issues/275/refer-consistently-to-project-as-coverage .. _issue 313: https://bitbucket.org/ned/coveragepy/issues/313/add-license-file-containing-2-3-or-4 .. _issue 380: https://bitbucket.org/ned/coveragepy/issues/380/code-executed-by-exec-excluded-from .. _issue 385: https://bitbucket.org/ned/coveragepy/issues/385/coverage-combine-doesnt-work-with-rcfile .. _issue 386: https://bitbucket.org/ned/coveragepy/issues/386/error-on-unrecognised-configuration .. _issue 387: https://bitbucket.org/ned/coveragepy/issues/387/performance-degradation-from-371-to-40 .. 40 issues closed in 4.0 below here Version 4.0a6 --- 2015-06-21 ---------------------------- - Python 3.5b2 and PyPy 2.6.0 are supported. - The original module-level function interface to coverage.py is no longer supported. You must now create a ``coverage.Coverage`` object, and use methods on it. - The ``coverage combine`` command now accepts any number of directories as arguments, and will combine all the data files from those directories. This means you don't have to copy the files to one directory before combining. Thanks, Christine Lytwynec. Finishes `issue 354`_. - Branch coverage couldn't properly handle certain extremely long files. This is now fixed (`issue 359`_). - Branch coverage didn't understand yield statements properly. Mickie Betz persisted in pursuing this despite Ned's pessimism. Fixes `issue 308`_ and `issue 324`_. - The COVERAGE_DEBUG environment variable can be used to set the ``[run] debug`` configuration option to control what internal operations are logged. - HTML reports were truncated at formfeed characters. This is now fixed (`issue 360`_). It's always fun when the problem is due to a `bug in the Python standard library `_. - Files with incorrect encoding declaration comments are no longer ignored by the reporting commands, fixing `issue 351`_. - HTML reports now include a timestamp in the footer, closing `issue 299`_. Thanks, Conrad Ho. - HTML reports now begrudgingly use double-quotes rather than single quotes, because there are "software engineers" out there writing tools that read HTML and somehow have no idea that single quotes exist. Capitulates to the absurd `issue 361`_. Thanks, Jon Chappell. - The ``coverage annotate`` command now handles non-ASCII characters properly, closing `issue 363`_. Thanks, Leonardo Pistone. - Drive letters on Windows were not normalized correctly, now they are. Thanks, Ionel Cristian Mărieș. - Plugin support had some bugs fixed, closing `issue 374`_ and `issue 375`_. Thanks, Stefan Behnel. .. _issue 299: https://bitbucket.org/ned/coveragepy/issues/299/inserted-created-on-yyyy-mm-dd-hh-mm-in .. _issue 308: https://bitbucket.org/ned/coveragepy/issues/308/yield-lambda-branch-coverage .. _issue 324: https://bitbucket.org/ned/coveragepy/issues/324/yield-in-loop-confuses-branch-coverage .. _issue 351: https://bitbucket.org/ned/coveragepy/issues/351/files-with-incorrect-encoding-are-ignored .. _issue 354: https://bitbucket.org/ned/coveragepy/issues/354/coverage-combine-should-take-a-list-of .. _issue 359: https://bitbucket.org/ned/coveragepy/issues/359/xml-report-chunk-error .. _issue 360: https://bitbucket.org/ned/coveragepy/issues/360/html-reports-get-confused-by-l-in-the-code .. _issue 361: https://bitbucket.org/ned/coveragepy/issues/361/use-double-quotes-in-html-output-to .. _issue 363: https://bitbucket.org/ned/coveragepy/issues/363/annotate-command-hits-unicode-happy-fun .. _issue 374: https://bitbucket.org/ned/coveragepy/issues/374/c-tracer-lookups-fail-in .. _issue 375: https://bitbucket.org/ned/coveragepy/issues/375/ctracer_handle_return-reads-byte-code Version 4.0a5 --- 2015-02-16 ---------------------------- - Plugin support is now implemented in the C tracer instead of the Python tracer. This greatly improves the speed of tracing projects using plugins. - Coverage.py now always adds the current directory to sys.path, so that plugins can import files in the current directory (`issue 358`_). - If the `config_file` argument to the Coverage constructor is specified as ".coveragerc", it is treated as if it were True. This means setup.cfg is also examined, and a missing file is not considered an error (`issue 357`_). - Wildly experimental: support for measuring processes started by the multiprocessing module. To use, set ``--concurrency=multiprocessing``, either on the command line or in the .coveragerc file (`issue 117`_). Thanks, Eduardo Schettino. Currently, this does not work on Windows. - A new warning is possible, if a desired file isn't measured because it was imported before coverage.py was started (`issue 353`_). - The `coverage.process_startup` function now will start coverage measurement only once, no matter how many times it is called. This fixes problems due to unusual virtualenv configurations (`issue 340`_). - Added 3.5.0a1 to the list of supported CPython versions. .. _issue 117: https://bitbucket.org/ned/coveragepy/issues/117/enable-coverage-measurement-of-code-run-by .. _issue 340: https://bitbucket.org/ned/coveragepy/issues/340/keyerror-subpy .. _issue 353: https://bitbucket.org/ned/coveragepy/issues/353/40a3-introduces-an-unexpected-third-case .. _issue 357: https://bitbucket.org/ned/coveragepy/issues/357/behavior-changed-when-coveragerc-is .. _issue 358: https://bitbucket.org/ned/coveragepy/issues/358/all-coverage-commands-should-adjust Version 4.0a4 --- 2015-01-25 ---------------------------- - Plugins can now provide sys_info for debugging output. - Started plugins documentation. - Prepared to move the docs to readthedocs.org. Version 4.0a3 --- 2015-01-20 ---------------------------- - Reports now use file names with extensions. Previously, a report would describe a/b/c.py as "a/b/c". Now it is shown as "a/b/c.py". This allows for better support of non-Python files, and also fixed `issue 69`_. - The XML report now reports each directory as a package again. This was a bad regression, I apologize. This was reported in `issue 235`_, which is now fixed. - A new configuration option for the XML report: ``[xml] package_depth`` controls which directories are identified as packages in the report. Directories deeper than this depth are not reported as packages. The default is that all directories are reported as packages. Thanks, Lex Berezhny. - When looking for the source for a frame, check if the file exists. On Windows, .pyw files are no longer recorded as .py files. Along the way, this fixed `issue 290`_. - Empty files are now reported as 100% covered in the XML report, not 0% covered (`issue 345`_). - Regexes in the configuration file are now compiled as soon as they are read, to provide error messages earlier (`issue 349`_). .. _issue 69: https://bitbucket.org/ned/coveragepy/issues/69/coverage-html-overwrite-files-that-doesnt .. _issue 235: https://bitbucket.org/ned/coveragepy/issues/235/package-name-is-missing-in-xml-report .. _issue 290: https://bitbucket.org/ned/coveragepy/issues/290/running-programmatically-with-pyw-files .. _issue 345: https://bitbucket.org/ned/coveragepy/issues/345/xml-reports-line-rate-0-for-empty-files .. _issue 349: https://bitbucket.org/ned/coveragepy/issues/349/bad-regex-in-config-should-get-an-earlier Version 4.0a2 --- 2015-01-14 ---------------------------- - Officially support PyPy 2.4, and PyPy3 2.4. Drop support for CPython 3.2 and older versions of PyPy. The code won't work on CPython 3.2. It will probably still work on older versions of PyPy, but I'm not testing against them. - Plugins! - The original command line switches (`-x` to run a program, etc) are no longer supported. - A new option: `coverage report --skip-covered` will reduce the number of files reported by skipping files with 100% coverage. Thanks, Krystian Kichewko. This means that empty `__init__.py` files will be skipped, since they are 100% covered, closing `issue 315`_. - You can now specify the ``--fail-under`` option in the ``.coveragerc`` file as the ``[report] fail_under`` option. This closes `issue 314`_. - The ``COVERAGE_OPTIONS`` environment variable is no longer supported. It was a hack for ``--timid`` before configuration files were available. - The HTML report now has filtering. Type text into the Filter box on the index page, and only modules with that text in the name will be shown. Thanks, Danny Allen. - The textual report and the HTML report used to report partial branches differently for no good reason. Now the text report's "missing branches" column is a "partial branches" column so that both reports show the same numbers. This closes `issue 342`_. - If you specify a ``--rcfile`` that cannot be read, you will get an error message. Fixes `issue 343`_. - The ``--debug`` switch can now be used on any command. - You can now programmatically adjust the configuration of coverage.py by setting items on `Coverage.config` after construction. - A module run with ``-m`` can be used as the argument to ``--source``, fixing `issue 328`_. Thanks, Buck Evan. - The regex for matching exclusion pragmas has been fixed to allow more kinds of whitespace, fixing `issue 334`_. - Made some PyPy-specific tweaks to improve speed under PyPy. Thanks, Alex Gaynor. - In some cases, with a source file missing a final newline, coverage.py would count statements incorrectly. This is now fixed, closing `issue 293`_. - The status.dat file that HTML reports use to avoid re-creating files that haven't changed is now a JSON file instead of a pickle file. This obviates `issue 287`_ and `issue 237`_. .. _issue 237: https://bitbucket.org/ned/coveragepy/issues/237/htmlcov-with-corrupt-statusdat .. _issue 287: https://bitbucket.org/ned/coveragepy/issues/287/htmlpy-doesnt-specify-pickle-protocol .. _issue 293: https://bitbucket.org/ned/coveragepy/issues/293/number-of-statement-detection-wrong-if-no .. _issue 314: https://bitbucket.org/ned/coveragepy/issues/314/fail_under-param-not-working-in-coveragerc .. _issue 315: https://bitbucket.org/ned/coveragepy/issues/315/option-to-omit-empty-files-eg-__init__py .. _issue 328: https://bitbucket.org/ned/coveragepy/issues/328/misbehavior-in-run-source .. _issue 334: https://bitbucket.org/ned/coveragepy/issues/334/pragma-not-recognized-if-tab-character .. _issue 342: https://bitbucket.org/ned/coveragepy/issues/342/console-and-html-coverage-reports-differ .. _issue 343: https://bitbucket.org/ned/coveragepy/issues/343/an-explicitly-named-non-existent-config Version 4.0a1 --- 2014-09-27 ---------------------------- - Python versions supported are now CPython 2.6, 2.7, 3.2, 3.3, and 3.4, and PyPy 2.2. - Gevent, eventlet, and greenlet are now supported, closing `issue 149`_. The ``concurrency`` setting specifies the concurrency library in use. Huge thanks to Peter Portante for initial implementation, and to Joe Jevnik for the final insight that completed the work. - Options are now also read from a setup.cfg file, if any. Sections are prefixed with "coverage:", so the ``[run]`` options will be read from the ``[coverage:run]`` section of setup.cfg. Finishes `issue 304`_. - The ``report -m`` command can now show missing branches when reporting on branch coverage. Thanks, Steve Leonard. Closes `issue 230`_. - The XML report now contains a element, fixing `issue 94`_. Thanks Stan Hu. - The class defined in the coverage module is now called ``Coverage`` instead of ``coverage``, though the old name still works, for backward compatibility. - The ``fail-under`` value is now rounded the same as reported results, preventing paradoxical results, fixing `issue 284`_. - The XML report will now create the output directory if need be, fixing `issue 285`_. Thanks, Chris Rose. - HTML reports no longer raise UnicodeDecodeError if a Python file has undecodable characters, fixing `issue 303`_ and `issue 331`_. - The annotate command will now annotate all files, not just ones relative to the current directory, fixing `issue 57`_. - The coverage module no longer causes deprecation warnings on Python 3.4 by importing the imp module, fixing `issue 305`_. - Encoding declarations in source files are only considered if they are truly comments. Thanks, Anthony Sottile. .. _issue 57: https://bitbucket.org/ned/coveragepy/issues/57/annotate-command-fails-to-annotate-many .. _issue 94: https://bitbucket.org/ned/coveragepy/issues/94/coverage-xml-doesnt-produce-sources .. _issue 149: https://bitbucket.org/ned/coveragepy/issues/149/coverage-gevent-looks-broken .. _issue 230: https://bitbucket.org/ned/coveragepy/issues/230/show-line-no-for-missing-branches-in .. _issue 284: https://bitbucket.org/ned/coveragepy/issues/284/fail-under-should-show-more-precision .. _issue 285: https://bitbucket.org/ned/coveragepy/issues/285/xml-report-fails-if-output-file-directory .. _issue 303: https://bitbucket.org/ned/coveragepy/issues/303/unicodedecodeerror .. _issue 304: https://bitbucket.org/ned/coveragepy/issues/304/attempt-to-get-configuration-from-setupcfg .. _issue 305: https://bitbucket.org/ned/coveragepy/issues/305/pendingdeprecationwarning-the-imp-module .. _issue 331: https://bitbucket.org/ned/coveragepy/issues/331/failure-of-encoding-detection-on-python2 .. _changes_371: Version 3.7.1 --- 2013-12-13 ---------------------------- - Improved the speed of HTML report generation by about 20%. - Fixed the mechanism for finding OS-installed static files for the HTML report so that it will actually find OS-installed static files. .. _changes_37: Version 3.7 --- 2013-10-06 -------------------------- - Added the ``--debug`` switch to ``coverage run``. It accepts a list of options indicating the type of internal activity to log to stderr. - Improved the branch coverage facility, fixing `issue 92`_ and `issue 175`_. - Running code with ``coverage run -m`` now behaves more like Python does, setting sys.path properly, which fixes `issue 207`_ and `issue 242`_. - Coverage.py can now run .pyc files directly, closing `issue 264`_. - Coverage.py properly supports .pyw files, fixing `issue 261`_. - Omitting files within a tree specified with the ``source`` option would cause them to be incorrectly marked as unexecuted, as described in `issue 218`_. This is now fixed. - When specifying paths to alias together during data combining, you can now specify relative paths, fixing `issue 267`_. - Most file paths can now be specified with username expansion (``~/src``, or ``~build/src``, for example), and with environment variable expansion (``build/$BUILDNUM/src``). - Trying to create an XML report with no files to report on, would cause a ZeroDivideError, but no longer does, fixing `issue 250`_. - When running a threaded program under the Python tracer, coverage.py no longer issues a spurious warning about the trace function changing: "Trace function changed, measurement is likely wrong: None." This fixes `issue 164`_. - Static files necessary for HTML reports are found in system-installed places, to ease OS-level packaging of coverage.py. Closes `issue 259`_. - Source files with encoding declarations, but a blank first line, were not decoded properly. Now they are. Thanks, Roger Hu. - The source kit now includes the ``__main__.py`` file in the root coverage directory, fixing `issue 255`_. .. _issue 92: https://bitbucket.org/ned/coveragepy/issues/92/finally-clauses-arent-treated-properly-in .. _issue 164: https://bitbucket.org/ned/coveragepy/issues/164/trace-function-changed-warning-when-using .. _issue 175: https://bitbucket.org/ned/coveragepy/issues/175/branch-coverage-gets-confused-in-certain .. _issue 207: https://bitbucket.org/ned/coveragepy/issues/207/run-m-cannot-find-module-or-package-in .. _issue 242: https://bitbucket.org/ned/coveragepy/issues/242/running-a-two-level-package-doesnt-work .. _issue 218: https://bitbucket.org/ned/coveragepy/issues/218/run-command-does-not-respect-the-omit-flag .. _issue 250: https://bitbucket.org/ned/coveragepy/issues/250/uncaught-zerodivisionerror-when-generating .. _issue 255: https://bitbucket.org/ned/coveragepy/issues/255/directory-level-__main__py-not-included-in .. _issue 259: https://bitbucket.org/ned/coveragepy/issues/259/allow-use-of-system-installed-third-party .. _issue 261: https://bitbucket.org/ned/coveragepy/issues/261/pyw-files-arent-reported-properly .. _issue 264: https://bitbucket.org/ned/coveragepy/issues/264/coverage-wont-run-pyc-files .. _issue 267: https://bitbucket.org/ned/coveragepy/issues/267/relative-path-aliases-dont-work .. _changes_36: Version 3.6 --- 2013-01-05 -------------------------- - Added a page to the docs about troublesome situations, closing `issue 226`_, and added some info to the TODO file, closing `issue 227`_. .. _issue 226: https://bitbucket.org/ned/coveragepy/issues/226/make-readme-section-to-describe-when .. _issue 227: https://bitbucket.org/ned/coveragepy/issues/227/update-todo Version 3.6b3 --- 2012-12-29 ---------------------------- - Beta 2 broke the nose plugin. It's fixed again, closing `issue 224`_. .. _issue 224: https://bitbucket.org/ned/coveragepy/issues/224/36b2-breaks-nosexcover Version 3.6b2 --- 2012-12-23 ---------------------------- - Coverage.py runs on Python 2.3 and 2.4 again. It was broken in 3.6b1. - The C extension is optionally compiled using a different more widely-used technique, taking another stab at fixing `issue 80`_ once and for all. - Combining data files would create entries for phantom files if used with ``source`` and path aliases. It no longer does. - ``debug sys`` now shows the configuration file path that was read. - If an oddly-behaved package claims that code came from an empty-string file name, coverage.py no longer associates it with the directory name, fixing `issue 221`_. .. _issue 221: https://bitbucket.org/ned/coveragepy/issues/221/coveragepy-incompatible-with-pyratemp Version 3.6b1 --- 2012-11-28 ---------------------------- - Wildcards in ``include=`` and ``omit=`` arguments were not handled properly in reporting functions, though they were when running. Now they are handled uniformly, closing `issue 143`_ and `issue 163`_. **NOTE**: it is possible that your configurations may now be incorrect. If you use ``include`` or ``omit`` during reporting, whether on the command line, through the API, or in a configuration file, please check carefully that you were not relying on the old broken behavior. - The **report**, **html**, and **xml** commands now accept a ``--fail-under`` switch that indicates in the exit status whether the coverage percentage was less than a particular value. Closes `issue 139`_. - The reporting functions coverage.report(), coverage.html_report(), and coverage.xml_report() now all return a float, the total percentage covered measurement. - The HTML report's title can now be set in the configuration file, with the ``--title`` switch on the command line, or via the API. - Configuration files now support substitution of environment variables, using syntax like ``${WORD}``. Closes `issue 97`_. - Embarrassingly, the ``[xml] output=`` setting in the .coveragerc file simply didn't work. Now it does. - The XML report now consistently uses file names for the file name attribute, rather than sometimes using module names. Fixes `issue 67`_. Thanks, Marcus Cobden. - Coverage percentage metrics are now computed slightly differently under branch coverage. This means that completely unexecuted files will now correctly have 0% coverage, fixing `issue 156`_. This also means that your total coverage numbers will generally now be lower if you are measuring branch coverage. - When installing, now in addition to creating a "coverage" command, two new aliases are also installed. A "coverage2" or "coverage3" command will be created, depending on whether you are installing in Python 2.x or 3.x. A "coverage-X.Y" command will also be created corresponding to your specific version of Python. Closes `issue 111`_. - The coverage.py installer no longer tries to bootstrap setuptools or Distribute. You must have one of them installed first, as `issue 202`_ recommended. - The coverage.py kit now includes docs (closing `issue 137`_) and tests. - On Windows, files are now reported in their correct case, fixing `issue 89`_ and `issue 203`_. - If a file is missing during reporting, the path shown in the error message is now correct, rather than an incorrect path in the current directory. Fixes `issue 60`_. - Running an HTML report in Python 3 in the same directory as an old Python 2 HTML report would fail with a UnicodeDecodeError. This issue (`issue 193`_) is now fixed. - Fixed yet another error trying to parse non-Python files as Python, this time an IndentationError, closing `issue 82`_ for the fourth time... - If `coverage xml` fails because there is no data to report, it used to create a zero-length XML file. Now it doesn't, fixing `issue 210`_. - Jython files now work with the ``--source`` option, fixing `issue 100`_. - Running coverage.py under a debugger is unlikely to work, but it shouldn't fail with "TypeError: 'NoneType' object is not iterable". Fixes `issue 201`_. - On some Linux distributions, when installed with the OS package manager, coverage.py would report its own code as part of the results. Now it won't, fixing `issue 214`_, though this will take some time to be repackaged by the operating systems. - Docstrings for the legacy singleton methods are more helpful. Thanks Marius Gedminas. Closes `issue 205`_. - The pydoc tool can now show documentation for the class `coverage.coverage`. Closes `issue 206`_. - Added a page to the docs about contributing to coverage.py, closing `issue 171`_. - When coverage.py ended unsuccessfully, it may have reported odd errors like ``'NoneType' object has no attribute 'isabs'``. It no longer does, so kiss `issue 153`_ goodbye. .. _issue 60: https://bitbucket.org/ned/coveragepy/issues/60/incorrect-path-to-orphaned-pyc-files .. _issue 67: https://bitbucket.org/ned/coveragepy/issues/67/xml-report-filenames-may-be-generated .. _issue 89: https://bitbucket.org/ned/coveragepy/issues/89/on-windows-all-packages-are-reported-in .. _issue 97: https://bitbucket.org/ned/coveragepy/issues/97/allow-environment-variables-to-be .. _issue 100: https://bitbucket.org/ned/coveragepy/issues/100/source-directive-doesnt-work-for-packages .. _issue 111: https://bitbucket.org/ned/coveragepy/issues/111/when-installing-coverage-with-pip-not .. _issue 137: https://bitbucket.org/ned/coveragepy/issues/137/provide-docs-with-source-distribution .. _issue 139: https://bitbucket.org/ned/coveragepy/issues/139/easy-check-for-a-certain-coverage-in-tests .. _issue 143: https://bitbucket.org/ned/coveragepy/issues/143/omit-doesnt-seem-to-work-in-coverage .. _issue 153: https://bitbucket.org/ned/coveragepy/issues/153/non-existent-filename-triggers .. _issue 156: https://bitbucket.org/ned/coveragepy/issues/156/a-completely-unexecuted-file-shows-14 .. _issue 163: https://bitbucket.org/ned/coveragepy/issues/163/problem-with-include-and-omit-filename .. _issue 171: https://bitbucket.org/ned/coveragepy/issues/171/how-to-contribute-and-run-tests .. _issue 193: https://bitbucket.org/ned/coveragepy/issues/193/unicodedecodeerror-on-htmlpy .. _issue 201: https://bitbucket.org/ned/coveragepy/issues/201/coverage-using-django-14-with-pydb-on .. _issue 202: https://bitbucket.org/ned/coveragepy/issues/202/get-rid-of-ez_setuppy-and .. _issue 203: https://bitbucket.org/ned/coveragepy/issues/203/duplicate-filenames-reported-when-filename .. _issue 205: https://bitbucket.org/ned/coveragepy/issues/205/make-pydoc-coverage-more-friendly .. _issue 206: https://bitbucket.org/ned/coveragepy/issues/206/pydoc-coveragecoverage-fails-with-an-error .. _issue 210: https://bitbucket.org/ned/coveragepy/issues/210/if-theres-no-coverage-data-coverage-xml .. _issue 214: https://bitbucket.org/ned/coveragepy/issues/214/coveragepy-measures-itself-on-precise .. _changes_353: Version 3.5.3 --- 2012-09-29 ---------------------------- - Line numbers in the HTML report line up better with the source lines, fixing `issue 197`_, thanks Marius Gedminas. - When specifying a directory as the source= option, the directory itself no longer needs to have a ``__init__.py`` file, though its sub-directories do, to be considered as source files. - Files encoded as UTF-8 with a BOM are now properly handled, fixing `issue 179`_. Thanks, Pablo Carballo. - Fixed more cases of non-Python files being reported as Python source, and then not being able to parse them as Python. Closes `issue 82`_ (again). Thanks, Julian Berman. - Fixed memory leaks under Python 3, thanks, Brett Cannon. Closes `issue 147`_. - Optimized .pyo files may not have been handled correctly, `issue 195`_. Thanks, Marius Gedminas. - Certain unusually named file paths could have been mangled during reporting, `issue 194`_. Thanks, Marius Gedminas. - Try to do a better job of the impossible task of detecting when we can't build the C extension, fixing `issue 183`_. - Testing is now done with `tox`_, thanks, Marc Abramowitz. .. _issue 147: https://bitbucket.org/ned/coveragepy/issues/147/massive-memory-usage-by-ctracer .. _issue 179: https://bitbucket.org/ned/coveragepy/issues/179/htmlreporter-fails-when-source-file-is .. _issue 183: https://bitbucket.org/ned/coveragepy/issues/183/install-fails-for-python-23 .. _issue 194: https://bitbucket.org/ned/coveragepy/issues/194/filelocatorrelative_filename-could-mangle .. _issue 195: https://bitbucket.org/ned/coveragepy/issues/195/pyo-file-handling-in-codeunit .. _issue 197: https://bitbucket.org/ned/coveragepy/issues/197/line-numbers-in-html-report-do-not-align .. _tox: https://tox.readthedocs.io/ .. _changes_352: Version 3.5.2 --- 2012-05-04 ---------------------------- No changes since 3.5.2.b1 Version 3.5.2b1 --- 2012-04-29 ------------------------------ - The HTML report has slightly tweaked controls: the buttons at the top of the page are color-coded to the source lines they affect. - Custom CSS can be applied to the HTML report by specifying a CSS file as the ``extra_css`` configuration value in the ``[html]`` section. - Source files with custom encodings declared in a comment at the top are now properly handled during reporting on Python 2. Python 3 always handled them properly. This fixes `issue 157`_. - Backup files left behind by editors are no longer collected by the source= option, fixing `issue 168`_. - If a file doesn't parse properly as Python, we don't report it as an error if the file name seems like maybe it wasn't meant to be Python. This is a pragmatic fix for `issue 82`_. - The ``-m`` switch on ``coverage report``, which includes missing line numbers in the summary report, can now be specified as ``show_missing`` in the config file. Closes `issue 173`_. - When running a module with ``coverage run -m ``, certain details of the execution environment weren't the same as for ``python -m ``. This had the unfortunate side-effect of making ``coverage run -m unittest discover`` not work if you had tests in a directory named "test". This fixes `issue 155`_ and `issue 142`_. - Now the exit status of your product code is properly used as the process status when running ``python -m coverage run ...``. Thanks, JT Olds. - When installing into pypy, we no longer attempt (and fail) to compile the C tracer function, closing `issue 166`_. .. _issue 142: https://bitbucket.org/ned/coveragepy/issues/142/executing-python-file-syspath-is-replaced .. _issue 155: https://bitbucket.org/ned/coveragepy/issues/155/cant-use-coverage-run-m-unittest-discover .. _issue 157: https://bitbucket.org/ned/coveragepy/issues/157/chokes-on-source-files-with-non-utf-8 .. _issue 166: https://bitbucket.org/ned/coveragepy/issues/166/dont-try-to-compile-c-extension-on-pypy .. _issue 168: https://bitbucket.org/ned/coveragepy/issues/168/dont-be-alarmed-by-emacs-droppings .. _issue 173: https://bitbucket.org/ned/coveragepy/issues/173/theres-no-way-to-specify-show-missing-in .. _changes_351: Version 3.5.1 --- 2011-09-23 ---------------------------- - The ``[paths]`` feature unfortunately didn't work in real world situations where you wanted to, you know, report on the combined data. Now all paths stored in the combined file are canonicalized properly. Version 3.5.1b1 --- 2011-08-28 ------------------------------ - When combining data files from parallel runs, you can now instruct coverage.py about which directories are equivalent on different machines. A ``[paths]`` section in the configuration file lists paths that are to be considered equivalent. Finishes `issue 17`_. - for-else constructs are understood better, and don't cause erroneous partial branch warnings. Fixes `issue 122`_. - Branch coverage for ``with`` statements is improved, fixing `issue 128`_. - The number of partial branches reported on the HTML summary page was different than the number reported on the individual file pages. This is now fixed. - An explicit include directive to measure files in the Python installation wouldn't work because of the standard library exclusion. Now the include directive takes precedence, and the files will be measured. Fixes `issue 138`_. - The HTML report now handles Unicode characters in Python source files properly. This fixes `issue 124`_ and `issue 144`_. Thanks, Devin Jeanpierre. - In order to help the core developers measure the test coverage of the standard library, Brandon Rhodes devised an aggressive hack to trick Python into running some coverage.py code before anything else in the process. See the coverage/fullcoverage directory if you are interested. .. _issue 17: https://bitbucket.org/ned/coveragepy/issues/17/support-combining-coverage-data-from .. _issue 122: https://bitbucket.org/ned/coveragepy/issues/122/for-else-always-reports-missing-branch .. _issue 124: https://bitbucket.org/ned/coveragepy/issues/124/no-arbitrary-unicode-in-html-reports-in .. _issue 128: https://bitbucket.org/ned/coveragepy/issues/128/branch-coverage-of-with-statement-in-27 .. _issue 138: https://bitbucket.org/ned/coveragepy/issues/138/include-should-take-precedence-over-is .. _issue 144: https://bitbucket.org/ned/coveragepy/issues/144/failure-generating-html-output-for .. _changes_35: Version 3.5 --- 2011-06-29 -------------------------- - The HTML report hotkeys now behave slightly differently when the current chunk isn't visible at all: a chunk on the screen will be selected, instead of the old behavior of jumping to the literal next chunk. The hotkeys now work in Google Chrome. Thanks, Guido van Rossum. Version 3.5b1 --- 2011-06-05 ---------------------------- - The HTML report now has hotkeys. Try ``n``, ``s``, ``m``, ``x``, ``b``, ``p``, and ``c`` on the overview page to change the column sorting. On a file page, ``r``, ``m``, ``x``, and ``p`` toggle the run, missing, excluded, and partial line markings. You can navigate the highlighted sections of code by using the ``j`` and ``k`` keys for next and previous. The ``1`` (one) key jumps to the first highlighted section in the file, and ``0`` (zero) scrolls to the top of the file. - The ``--omit`` and ``--include`` switches now interpret their values more usefully. If the value starts with a wildcard character, it is used as-is. If it does not, it is interpreted relative to the current directory. Closes `issue 121`_. - Partial branch warnings can now be pragma'd away. The configuration option ``partial_branches`` is a list of regular expressions. Lines matching any of those expressions will never be marked as a partial branch. In addition, there's a built-in list of regular expressions marking statements which should never be marked as partial. This list includes ``while True:``, ``while 1:``, ``if 1:``, and ``if 0:``. - The ``coverage()`` constructor accepts single strings for the ``omit=`` and ``include=`` arguments, adapting to a common error in programmatic use. - Modules can now be run directly using ``coverage run -m modulename``, to mirror Python's ``-m`` flag. Closes `issue 95`_, thanks, Brandon Rhodes. - ``coverage run`` didn't emulate Python accurately in one small detail: the current directory inserted into ``sys.path`` was relative rather than absolute. This is now fixed. - HTML reporting is now incremental: a record is kept of the data that produced the HTML reports, and only files whose data has changed will be generated. This should make most HTML reporting faster. - Pathological code execution could disable the trace function behind our backs, leading to incorrect code measurement. Now if this happens, coverage.py will issue a warning, at least alerting you to the problem. Closes `issue 93`_. Thanks to Marius Gedminas for the idea. - The C-based trace function now behaves properly when saved and restored with ``sys.gettrace()`` and ``sys.settrace()``. This fixes `issue 125`_ and `issue 123`_. Thanks, Devin Jeanpierre. - Source files are now opened with Python 3.2's ``tokenize.open()`` where possible, to get the best handling of Python source files with encodings. Closes `issue 107`_, thanks, Brett Cannon. - Syntax errors in supposed Python files can now be ignored during reporting with the ``-i`` switch just like other source errors. Closes `issue 115`_. - Installation from source now succeeds on machines without a C compiler, closing `issue 80`_. - Coverage.py can now be run directly from a working tree by specifying the directory name to python: ``python coverage_py_working_dir run ...``. Thanks, Brett Cannon. - A little bit of Jython support: `coverage run` can now measure Jython execution by adapting when $py.class files are traced. Thanks, Adi Roiban. Jython still doesn't provide the Python libraries needed to make coverage reporting work, unfortunately. - Internally, files are now closed explicitly, fixing `issue 104`_. Thanks, Brett Cannon. .. _issue 80: https://bitbucket.org/ned/coveragepy/issues/80/is-there-a-duck-typing-way-to-know-we-cant .. _issue 93: https://bitbucket.org/ned/coveragepy/issues/93/copying-a-mock-object-breaks-coverage .. _issue 95: https://bitbucket.org/ned/coveragepy/issues/95/run-subcommand-should-take-a-module-name .. _issue 104: https://bitbucket.org/ned/coveragepy/issues/104/explicitly-close-files .. _issue 107: https://bitbucket.org/ned/coveragepy/issues/107/codeparser-not-opening-source-files-with .. _issue 115: https://bitbucket.org/ned/coveragepy/issues/115/fail-gracefully-when-reporting-on-file .. _issue 121: https://bitbucket.org/ned/coveragepy/issues/121/filename-patterns-are-applied-stupidly .. _issue 123: https://bitbucket.org/ned/coveragepy/issues/123/pyeval_settrace-used-in-way-that-breaks .. _issue 125: https://bitbucket.org/ned/coveragepy/issues/125/coverage-removes-decoratortoolss-tracing .. _changes_34: Version 3.4 --- 2010-09-19 -------------------------- - The XML report is now sorted by package name, fixing `issue 88`_. - Programs that exited with ``sys.exit()`` with no argument weren't handled properly, producing a coverage.py stack trace. That is now fixed. .. _issue 88: https://bitbucket.org/ned/coveragepy/issues/88/xml-report-lists-packages-in-random-order Version 3.4b2 --- 2010-09-06 ---------------------------- - Completely unexecuted files can now be included in coverage results, reported as 0% covered. This only happens if the --source option is specified, since coverage.py needs guidance about where to look for source files. - The XML report output now properly includes a percentage for branch coverage, fixing `issue 65`_ and `issue 81`_. - Coverage percentages are now displayed uniformly across reporting methods. Previously, different reports could round percentages differently. Also, percentages are only reported as 0% or 100% if they are truly 0 or 100, and are rounded otherwise. Fixes `issue 41`_ and `issue 70`_. - The precision of reported coverage percentages can be set with the ``[report] precision`` config file setting. Completes `issue 16`_. - Threads derived from ``threading.Thread`` with an overridden `run` method would report no coverage for the `run` method. This is now fixed, closing `issue 85`_. .. _issue 16: https://bitbucket.org/ned/coveragepy/issues/16/allow-configuration-of-accuracy-of-percentage-totals .. _issue 41: https://bitbucket.org/ned/coveragepy/issues/41/report-says-100-when-it-isnt-quite-there .. _issue 65: https://bitbucket.org/ned/coveragepy/issues/65/branch-option-not-reported-in-cobertura .. _issue 70: https://bitbucket.org/ned/coveragepy/issues/70/text-report-and-html-report-disagree-on-coverage .. _issue 81: https://bitbucket.org/ned/coveragepy/issues/81/xml-report-does-not-have-condition-coverage-attribute-for-lines-with-a .. _issue 85: https://bitbucket.org/ned/coveragepy/issues/85/threadrun-isnt-measured Version 3.4b1 --- 2010-08-21 ---------------------------- - BACKWARD INCOMPATIBILITY: the ``--omit`` and ``--include`` switches now take file patterns rather than file prefixes, closing `issue 34`_ and `issue 36`_. - BACKWARD INCOMPATIBILITY: the `omit_prefixes` argument is gone throughout coverage.py, replaced with `omit`, a list of file name patterns suitable for `fnmatch`. A parallel argument `include` controls what files are included. - The run command now has a ``--source`` switch, a list of directories or module names. If provided, coverage.py will only measure execution in those source files. - Various warnings are printed to stderr for problems encountered during data measurement: if a ``--source`` module has no Python source to measure, or is never encountered at all, or if no data is collected. - The reporting commands (report, annotate, html, and xml) now have an ``--include`` switch to restrict reporting to modules matching those file patterns, similar to the existing ``--omit`` switch. Thanks, Zooko. - The run command now supports ``--include`` and ``--omit`` to control what modules it measures. This can speed execution and reduce the amount of data during reporting. Thanks Zooko. - Since coverage.py 3.1, using the Python trace function has been slower than it needs to be. A cache of tracing decisions was broken, but has now been fixed. - Python 2.7 and 3.2 have introduced new opcodes that are now supported. - Python files with no statements, for example, empty ``__init__.py`` files, are now reported as having zero statements instead of one. Fixes `issue 1`_. - Reports now have a column of missed line counts rather than executed line counts, since developers should focus on reducing the missed lines to zero, rather than increasing the executed lines to varying targets. Once suggested, this seemed blindingly obvious. - Line numbers in HTML source pages are clickable, linking directly to that line, which is highlighted on arrival. Added a link back to the index page at the bottom of each HTML page. - Programs that call ``os.fork`` will properly collect data from both the child and parent processes. Use ``coverage run -p`` to get two data files that can be combined with ``coverage combine``. Fixes `issue 56`_. - Coverage.py is now runnable as a module: ``python -m coverage``. Thanks, Brett Cannon. - When measuring code running in a virtualenv, most of the system library was being measured when it shouldn't have been. This is now fixed. - Doctest text files are no longer recorded in the coverage data, since they can't be reported anyway. Fixes `issue 52`_ and `issue 61`_. - Jinja HTML templates compile into Python code using the HTML file name, which confused coverage.py. Now these files are no longer traced, fixing `issue 82`_. - Source files can have more than one dot in them (foo.test.py), and will be treated properly while reporting. Fixes `issue 46`_. - Source files with DOS line endings are now properly tokenized for syntax coloring on non-DOS machines. Fixes `issue 53`_. - Unusual code structure that confused exits from methods with exits from classes is now properly analyzed. See `issue 62`_. - Asking for an HTML report with no files now shows a nice error message rather than a cryptic failure ('int' object is unsubscriptable). Fixes `issue 59`_. .. _issue 1: https://bitbucket.org/ned/coveragepy/issues/1/empty-__init__py-files-are-reported-as-1-executable .. _issue 34: https://bitbucket.org/ned/coveragepy/issues/34/enhanced-omit-globbing-handling .. _issue 36: https://bitbucket.org/ned/coveragepy/issues/36/provide-regex-style-omit .. _issue 46: https://bitbucket.org/ned/coveragepy/issues/46 .. _issue 53: https://bitbucket.org/ned/coveragepy/issues/53 .. _issue 52: https://bitbucket.org/ned/coveragepy/issues/52/doctesttestfile-confuses-source-detection .. _issue 56: https://bitbucket.org/ned/coveragepy/issues/56 .. _issue 61: https://bitbucket.org/ned/coveragepy/issues/61/annotate-i-doesnt-work .. _issue 62: https://bitbucket.org/ned/coveragepy/issues/62 .. _issue 59: https://bitbucket.org/ned/coveragepy/issues/59/html-report-fails-with-int-object-is .. _issue 82: https://bitbucket.org/ned/coveragepy/issues/82/tokenerror-when-generating-html-report .. _changes_331: Version 3.3.1 --- 2010-03-06 ---------------------------- - Using `parallel=True` in .coveragerc file prevented reporting, but now does not, fixing `issue 49`_. - When running your code with "coverage run", if you call `sys.exit()`, coverage.py will exit with that status code, fixing `issue 50`_. .. _issue 49: https://bitbucket.org/ned/coveragepy/issues/49 .. _issue 50: https://bitbucket.org/ned/coveragepy/issues/50 .. _changes_33: Version 3.3 --- 2010-02-24 -------------------------- - Settings are now read from a .coveragerc file. A specific file can be specified on the command line with --rcfile=FILE. The name of the file can be programmatically set with the `config_file` argument to the coverage() constructor, or reading a config file can be disabled with `config_file=False`. - Fixed a problem with nested loops having their branch possibilities mischaracterized: `issue 39`_. - Added coverage.process_start to enable coverage measurement when Python starts. - Parallel data file names now have a random number appended to them in addition to the machine name and process id. - Parallel data files combined with "coverage combine" are deleted after they're combined, to clean up unneeded files. Fixes `issue 40`_. - Exceptions thrown from product code run with "coverage run" are now displayed without internal coverage.py frames, so the output is the same as when the code is run without coverage.py. - The `data_suffix` argument to the coverage constructor is now appended with an added dot rather than simply appended, so that .coveragerc files will not be confused for data files. - Python source files that don't end with a newline can now be executed, fixing `issue 47`_. - Added an AUTHORS.txt file. .. _issue 39: https://bitbucket.org/ned/coveragepy/issues/39 .. _issue 40: https://bitbucket.org/ned/coveragepy/issues/40 .. _issue 47: https://bitbucket.org/ned/coveragepy/issues/47 .. _changes_32: Version 3.2 --- 2009-12-05 -------------------------- - Added a ``--version`` option on the command line. Version 3.2b4 --- 2009-12-01 ---------------------------- - Branch coverage improvements: - The XML report now includes branch information. - Click-to-sort HTML report columns are now persisted in a cookie. Viewing a report will sort it first the way you last had a coverage report sorted. Thanks, `Chris Adams`_. - On Python 3.x, setuptools has been replaced by `Distribute`_. .. _Distribute: https://pypi.python.org/pypi/distribute Version 3.2b3 --- 2009-11-23 ---------------------------- - Fixed a memory leak in the C tracer that was introduced in 3.2b1. - Branch coverage improvements: - Branches to excluded code are ignored. - The table of contents in the HTML report is now sortable: click the headers on any column. Thanks, `Chris Adams`_. .. _Chris Adams: http://chris.improbable.org Version 3.2b2 --- 2009-11-19 ---------------------------- - Branch coverage improvements: - Classes are no longer incorrectly marked as branches: `issue 32`_. - "except" clauses with types are no longer incorrectly marked as branches: `issue 35`_. - Fixed some problems syntax coloring sources with line continuations and source with tabs: `issue 30`_ and `issue 31`_. - The --omit option now works much better than before, fixing `issue 14`_ and `issue 33`_. Thanks, Danek Duvall. .. _issue 14: https://bitbucket.org/ned/coveragepy/issues/14 .. _issue 30: https://bitbucket.org/ned/coveragepy/issues/30 .. _issue 31: https://bitbucket.org/ned/coveragepy/issues/31 .. _issue 32: https://bitbucket.org/ned/coveragepy/issues/32 .. _issue 33: https://bitbucket.org/ned/coveragepy/issues/33 .. _issue 35: https://bitbucket.org/ned/coveragepy/issues/35 Version 3.2b1 --- 2009-11-10 ---------------------------- - Branch coverage! - XML reporting has file paths that let Cobertura find the source code. - The tracer code has changed, it's a few percent faster. - Some exceptions reported by the command line interface have been cleaned up so that tracebacks inside coverage.py aren't shown. Fixes `issue 23`_. .. _issue 23: https://bitbucket.org/ned/coveragepy/issues/23 .. _changes_31: Version 3.1 --- 2009-10-04 -------------------------- - Source code can now be read from eggs. Thanks, Ross Lawley. Fixes `issue 25`_. .. _issue 25: https://bitbucket.org/ned/coveragepy/issues/25 Version 3.1b1 --- 2009-09-27 ---------------------------- - Python 3.1 is now supported. - Coverage.py has a new command line syntax with sub-commands. This expands the possibilities for adding features and options in the future. The old syntax is still supported. Try "coverage help" to see the new commands. Thanks to Ben Finney for early help. - Added an experimental "coverage xml" command for producing coverage reports in a Cobertura-compatible XML format. Thanks, Bill Hart. - Added the --timid option to enable a simpler slower trace function that works for DecoratorTools projects, including TurboGears. Fixed `issue 12`_ and `issue 13`_. - HTML reports show modules from other directories. Fixed `issue 11`_. - HTML reports now display syntax-colored Python source. - Programs that change directory will still write .coverage files in the directory where execution started. Fixed `issue 24`_. - Added a "coverage debug" command for getting diagnostic information about the coverage.py installation. .. _issue 11: https://bitbucket.org/ned/coveragepy/issues/11 .. _issue 12: https://bitbucket.org/ned/coveragepy/issues/12 .. _issue 13: https://bitbucket.org/ned/coveragepy/issues/13 .. _issue 24: https://bitbucket.org/ned/coveragepy/issues/24 .. _changes_301: Version 3.0.1 --- 2009-07-07 ---------------------------- - Removed the recursion limit in the tracer function. Previously, code that ran more than 500 frames deep would crash. Fixed `issue 9`_. - Fixed a bizarre problem involving pyexpat, whereby lines following XML parser invocations could be overlooked. Fixed `issue 10`_. - On Python 2.3, coverage.py could mis-measure code with exceptions being raised. This is now fixed. - The coverage.py code itself will now not be measured by coverage.py, and no coverage.py modules will be mentioned in the nose --with-cover plug-in. Fixed `issue 8`_. - When running source files, coverage.py now opens them in universal newline mode just like Python does. This lets it run Windows files on Mac, for example. .. _issue 9: https://bitbucket.org/ned/coveragepy/issues/9 .. _issue 10: https://bitbucket.org/ned/coveragepy/issues/10 .. _issue 8: https://bitbucket.org/ned/coveragepy/issues/8 .. _changes_30: Version 3.0 --- 2009-06-13 -------------------------- - Fixed the way the Python library was ignored. Too much code was being excluded the old way. - Tabs are now properly converted in HTML reports. Previously indentation was lost. Fixed `issue 6`_. - Nested modules now get a proper flat_rootname. Thanks, Christian Heimes. .. _issue 6: https://bitbucket.org/ned/coveragepy/issues/6 Version 3.0b3 --- 2009-05-16 ---------------------------- - Added parameters to coverage.__init__ for options that had been set on the coverage object itself. - Added clear_exclude() and get_exclude_list() methods for programmatic manipulation of the exclude regexes. - Added coverage.load() to read previously-saved data from the data file. - Improved the finding of code files. For example, .pyc files that have been installed after compiling are now located correctly. Thanks, Detlev Offenbach. - When using the object API (that is, constructing a coverage() object), data is no longer saved automatically on process exit. You can re-enable it with the auto_data=True parameter on the coverage() constructor. The module-level interface still uses automatic saving. Version 3.0b --- 2009-04-30 --------------------------- HTML reporting, and continued refactoring. - HTML reports and annotation of source files: use the new -b (browser) switch. Thanks to George Song for code, inspiration and guidance. - Code in the Python standard library is not measured by default. If you need to measure standard library code, use the -L command-line switch during execution, or the cover_pylib=True argument to the coverage() constructor. - Source annotation into a directory (-a -d) behaves differently. The annotated files are named with their hierarchy flattened so that same-named files from different directories no longer collide. Also, only files in the current tree are included. - coverage.annotate_file is no longer available. - Programs executed with -x now behave more as they should, for example, __file__ has the correct value. - .coverage data files have a new pickle-based format designed for better extensibility. - Removed the undocumented cache_file argument to coverage.usecache(). Version 3.0b1 --- 2009-03-07 ---------------------------- Major overhaul. - Coverage.py is now a package rather than a module. Functionality has been split into classes. - The trace function is implemented in C for speed. Coverage.py runs are now much faster. Thanks to David Christian for productive micro-sprints and other encouragement. - Executable lines are identified by reading the line number tables in the compiled code, removing a great deal of complicated analysis code. - Precisely which lines are considered executable has changed in some cases. Therefore, your coverage stats may also change slightly. - The singleton coverage object is only created if the module-level functions are used. This maintains the old interface while allowing better programmatic use of Coverage.py. - The minimum supported Python version is 2.3. Version 2.85 --- 2008-09-14 --------------------------- - Add support for finding source files in eggs. Don't check for morf's being instances of ModuleType, instead use duck typing so that pseudo-modules can participate. Thanks, Imri Goldberg. - Use os.realpath as part of the fixing of file names so that symlinks won't confuse things. Thanks, Patrick Mezard. Version 2.80 --- 2008-05-25 --------------------------- - Open files in rU mode to avoid line ending craziness. Thanks, Edward Loper. Version 2.78 --- 2007-09-30 --------------------------- - Don't try to predict whether a file is Python source based on the extension. Extension-less files are often Pythons scripts. Instead, simply parse the file and catch the syntax errors. Hat tip to Ben Finney. Version 2.77 --- 2007-07-29 --------------------------- - Better packaging. Version 2.76 --- 2007-07-23 --------------------------- - Now Python 2.5 is *really* fully supported: the body of the new with statement is counted as executable. Version 2.75 --- 2007-07-22 --------------------------- - Python 2.5 now fully supported. The method of dealing with multi-line statements is now less sensitive to the exact line that Python reports during execution. Pass statements are handled specially so that their disappearance during execution won't throw off the measurement. Version 2.7 --- 2007-07-21 -------------------------- - "#pragma: nocover" is excluded by default. - Properly ignore docstrings and other constant expressions that appear in the middle of a function, a problem reported by Tim Leslie. - coverage.erase() shouldn't clobber the exclude regex. Change how parallel mode is invoked, and fix erase() so that it erases the cache when called programmatically. - In reports, ignore code executed from strings, since we can't do anything useful with it anyway. - Better file handling on Linux, thanks Guillaume Chazarain. - Better shell support on Windows, thanks Noel O'Boyle. - Python 2.2 support maintained, thanks Catherine Proulx. - Minor changes to avoid lint warnings. Version 2.6 --- 2006-08-23 -------------------------- - Applied Joseph Tate's patch for function decorators. - Applied Sigve Tjora and Mark van der Wal's fixes for argument handling. - Applied Geoff Bache's parallel mode patch. - Refactorings to improve testability. Fixes to command-line logic for parallel mode and collect. Version 2.5 --- 2005-12-04 -------------------------- - Call threading.settrace so that all threads are measured. Thanks Martin Fuzzey. - Add a file argument to report so that reports can be captured to a different destination. - Coverage.py can now measure itself. - Adapted Greg Rogers' patch for using relative file names, and sorting and omitting files to report on. Version 2.2 --- 2004-12-31 -------------------------- - Allow for keyword arguments in the module global functions. Thanks, Allen. Version 2.1 --- 2004-12-14 -------------------------- - Return 'analysis' to its original behavior and add 'analysis2'. Add a global for 'annotate', and factor it, adding 'annotate_file'. Version 2.0 --- 2004-12-12 -------------------------- Significant code changes. - Finding executable statements has been rewritten so that docstrings and other quirks of Python execution aren't mistakenly identified as missing lines. - Lines can be excluded from consideration, even entire suites of lines. - The file system cache of covered lines can be disabled programmatically. - Modernized the code. Earlier History --------------- 2001-12-04 GDR Created. 2001-12-06 GDR Added command-line interface and source code annotation. 2001-12-09 GDR Moved design and interface to separate documents. 2001-12-10 GDR Open cache file as binary on Windows. Allow simultaneous -e and -x, or -a and -r. 2001-12-12 GDR Added command-line help. Cache analysis so that it only needs to be done once when you specify -a and -r. 2001-12-13 GDR Improved speed while recording. Portable between Python 1.5.2 and 2.1.1. 2002-01-03 GDR Module-level functions work correctly. 2002-01-07 GDR Update sys.path when running a file with the -x option, so that it matches the value the program would get if it were run on its own. python-coverage-4.5+dfsg.1.orig/README.rst0000644000076600000620000000773213235412523016156 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt =========== Coverage.py =========== Code coverage testing for Python. | |license| |versions| |status| |docs| | |ci-status| |win-ci-status| |codecov| | |kit| |format| |saythanks| .. downloads badge seems to be broken... |downloads| Coverage.py measures code coverage, typically during test execution. It uses the code analysis tools and tracing hooks provided in the Python standard library to determine which lines are executable, and which have been executed. Coverage.py runs on many versions of Python: * CPython 2.6, 2.7 and 3.3 through 3.7. * PyPy2 5.10 and PyPy3 5.10. * Jython 2.7.1, though not for reporting. * IronPython 2.7.7, though not for reporting. Documentation is on `Read the Docs`_. Code repository and issue tracker are on `Bitbucket`_, with a mirrored repository on `GitHub`_. .. _Read the Docs: https://coverage.readthedocs.io/ .. _Bitbucket: https://bitbucket.org/ned/coveragepy .. _GitHub: https://github.com/nedbat/coveragepy **New in 4.5:** Configurator plug-ins. New in 4.4: Suppressable warnings, continuous coverage measurement. New in 4.3: HTML ``--skip-covered``, sys.excepthook support, tox.ini support. New in 4.2: better support for multiprocessing and combining data. New in 4.1: much-improved branch coverage. New in 4.0: ``--concurrency``, plugins for non-Python files, setup.cfg support, --skip-covered, HTML filtering, and more than 50 issues closed. Getting Started --------------- See the `Quick Start section`_ of the docs. .. _Quick Start section: https://coverage.readthedocs.io/#quick-start Contributing ------------ See the `Contributing section`_ of the docs. .. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html License ------- Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_. .. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 .. _NOTICE.txt: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. |ci-status| image:: https://travis-ci.org/nedbat/coveragepy.svg?branch=master :target: https://travis-ci.org/nedbat/coveragepy :alt: Build status .. |win-ci-status| image:: https://ci.appveyor.com/api/projects/status/kmeqpdje7h9r6vsf/branch/master?svg=true :target: https://ci.appveyor.com/project/nedbat/coveragepy :alt: Windows build status .. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat :target: https://coverage.readthedocs.io/ :alt: Documentation .. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master :alt: Requirements status .. |kit| image:: https://badge.fury.io/py/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: PyPI status .. |format| image:: https://img.shields.io/pypi/format/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Kit format .. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Weekly PyPI downloads .. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Python versions supported .. |status| image:: https://img.shields.io/pypi/status/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Package stability .. |license| image:: https://img.shields.io/pypi/l/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: License .. |codecov| image:: http://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2 :target: http://codecov.io/github/nedbat/coveragepy?branch=master :alt: Coverage! .. |saythanks| image:: https://img.shields.io/badge/saythanks.io-%E2%98%BC-1EAEDB.svg :target: https://saythanks.io/to/nedbat :alt: Say thanks :) python-coverage-4.5+dfsg.1.orig/CONTRIBUTORS.txt0000644000076600000620000000314213231142262017150 0ustar staffCoverage.py was originally written by Gareth Rees, and since 2004 has been extended and maintained by Ned Batchelder. Other contributions, including writing code, updating docs, and submitting useful bug reports, have been made by: Adi Roiban Alex Gaynor Alex Groce Alex Sandro Alexander Todorov Andrew Hoos Anthony Sottile Arcadiy Ivanov Aron Griffis Artem Dayneko Ben Finney Bill Hart Brandon Rhodes Brett Cannon Buck Evan Calen Pennington Carl Gieringer Catherine Proulx Chris Adams Chris Jerdonek Chris Rose Chris Warrick Christian Heimes Christine Lytwynec Christoph Zwerschke Conrad Ho Cosimo Lupo Dan Riti Dan Wandschneider Danek Duvall Daniel Hahler Danny Allen David Christian David MacIver David Stanek Detlev Offenbach Devin Jeanpierre Dirk Thomas Dmitry Shishov Dmitry Trofimov Eduardo Schettino Emil Madsen Edward Loper Geoff Bache George Paci George Song Greg Rogers Guillaume Chazarain Ilia Meerovich Imri Goldberg Ionel Cristian Mărieș JT Olds Jessamyn Smith Joe Doherty Jon Chappell Jon Dufresne Joseph Tate Josh Williams Julian Berman Krystian Kichewko Kyle Altendorf Lars Hupfeldt Nielsen Leonardo Pistone Lex Berezhny Loïc Dachary Marc Abramowitz Marcus Cobden Mark van der Wal Martin Fuzzey Matthew Boehm Matthew Desmarais Max Linke Mickie Betz Nathan Land Noel O'Boyle Olivier Grisel Pablo Carballo Patrick Mezard Peter Baughman Peter Ebden Peter Portante Rodrigue Cloutier Roger Hu Ross Lawley Roy Williams Sandra Martocchia Scott Belden Sigve Tjora Stan Hu Stefan Behnel Stephen Finucane Steve Leonard Steve Peak Ted Wexler Titus Brown Ville Skyttä Yury Selivanov Zac Hatfield-Dodds Zooko Wilcox-O'Hearn python-coverage-4.5+dfsg.1.orig/ci/0000755000076600000620000000000013235414514015053 5ustar staffpython-coverage-4.5+dfsg.1.orig/ci/manylinux.sh0000755000076600000620000000240113177624110017433 0ustar staff#!/bin/bash # From: https://github.com/pypa/python-manylinux-demo/blob/master/travis/build-wheels.sh # which is in the public domain. # # This is run inside a CentOS 5 virtual machine to build manylinux wheels: # # $ docker run -v `pwd`:/io quay.io/pypa/manylinux1_x86_64 /io/ci/build_manylinux.sh # set -e -x action=$1 shift if [[ $action == "build" ]]; then # Compile wheels cd /io for PYBIN in /opt/python/*/bin; do "$PYBIN/pip" install -r requirements/wheel.pip "$PYBIN/python" setup.py clean -a "$PYBIN/python" setup.py bdist_wheel -d ~/wheelhouse/ done cd ~ # Bundle external shared libraries into the wheels for whl in wheelhouse/*.whl; do auditwheel repair "$whl" -w /io/dist/ done elif [[ $action == "test" ]]; then # Create "pythonX.Y" links for PYBIN in /opt/python/*/bin/; do PYNAME=$("$PYBIN/python" -c "import sys; print('python{0[0]}.{0[1]}'.format(sys.version_info))") ln -sf "$PYBIN/$PYNAME" /usr/local/bin/$PYNAME done # Install packages and test TOXBIN=/opt/python/cp27-cp27m/bin "$TOXBIN/pip" install -r /io/requirements/ci.pip cd /io TOXWORKDIR=.tox_linux "$TOXBIN/tox" "$@" || true cd ~ else echo "Need an action to perform!" fi python-coverage-4.5+dfsg.1.orig/ci/install.ps10000644000076600000620000001617013146037571017160 0ustar staff# From: https://github.com/ogrisel/python-appveyor-demo/blob/master/appveyor/install.ps1 # # # Sample script to install Python and pip under Windows # Authors: Olivier Grisel, Jonathan Helmus, Kyle Kastner, and Alex Willmer # License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ $MINICONDA_URL = "http://repo.continuum.io/miniconda/" $BASE_URL = "https://www.python.org/ftp/python/" $GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" $GET_PIP_PATH = "C:\get-pip.py" $PYTHON_PRERELEASE_REGEX = @" (?x) (?\d+) \. (?\d+) \. (?\d+) (?[a-z]{1,2}\d+) "@ function Download ($filename, $url) { $webclient = New-Object System.Net.WebClient $basedir = $pwd.Path + "\" $filepath = $basedir + $filename if (Test-Path $filename) { Write-Host "Reusing" $filepath return $filepath } # Download and retry up to 3 times in case of network transient errors. Write-Host "Downloading" $filename "from" $url $retry_attempts = 2 for ($i = 0; $i -lt $retry_attempts; $i++) { try { $webclient.DownloadFile($url, $filepath) break } Catch [Exception]{ Start-Sleep 1 } } if (Test-Path $filepath) { Write-Host "File saved at" $filepath } else { # Retry once to get the error message if any at the last try $webclient.DownloadFile($url, $filepath) } return $filepath } function ParsePythonVersion ($python_version) { if ($python_version -match $PYTHON_PRERELEASE_REGEX) { return ([int]$matches.major, [int]$matches.minor, [int]$matches.micro, $matches.prerelease) } $version_obj = [version]$python_version return ($version_obj.major, $version_obj.minor, $version_obj.build, "") } function DownloadPython ($python_version, $platform_suffix) { $major, $minor, $micro, $prerelease = ParsePythonVersion $python_version if (($major -le 2 -and $micro -eq 0) ` -or ($major -eq 3 -and $minor -le 2 -and $micro -eq 0) ` ) { $dir = "$major.$minor" $python_version = "$major.$minor$prerelease" } else { $dir = "$major.$minor.$micro" } if ($prerelease) { if (($major -le 2) ` -or ($major -eq 3 -and $minor -eq 1) ` -or ($major -eq 3 -and $minor -eq 2) ` -or ($major -eq 3 -and $minor -eq 3) ` ) { $dir = "$dir/prev" } } if (($major -le 2) -or ($major -le 3 -and $minor -le 4)) { $ext = "msi" if ($platform_suffix) { $platform_suffix = ".$platform_suffix" } } else { $ext = "exe" if ($platform_suffix) { $platform_suffix = "-$platform_suffix" } } $filename = "python-$python_version$platform_suffix.$ext" $url = "$BASE_URL$dir/$filename" $filepath = Download $filename $url return $filepath } function InstallPython ($python_version, $architecture, $python_home) { Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home if (Test-Path $python_home) { Write-Host $python_home "already exists, skipping." return $false } if ($architecture -eq "32") { $platform_suffix = "" } else { $platform_suffix = "amd64" } $installer_path = DownloadPython $python_version $platform_suffix $installer_ext = [System.IO.Path]::GetExtension($installer_path) Write-Host "Installing $installer_path to $python_home" $install_log = $python_home + ".log" if ($installer_ext -eq '.msi') { InstallPythonMSI $installer_path $python_home $install_log } else { InstallPythonEXE $installer_path $python_home $install_log } if (Test-Path $python_home) { Write-Host "Python $python_version ($architecture) installation complete" } else { Write-Host "Failed to install Python in $python_home" Get-Content -Path $install_log Exit 1 } } function InstallPythonEXE ($exepath, $python_home, $install_log) { $install_args = "/quiet InstallAllUsers=1 TargetDir=$python_home" RunCommand $exepath $install_args } function InstallPythonMSI ($msipath, $python_home, $install_log) { $install_args = "/qn /log $install_log /i $msipath TARGETDIR=$python_home" $uninstall_args = "/qn /x $msipath" RunCommand "msiexec.exe" $install_args if (-not(Test-Path $python_home)) { Write-Host "Python seems to be installed else-where, reinstalling." RunCommand "msiexec.exe" $uninstall_args RunCommand "msiexec.exe" $install_args } } function RunCommand ($command, $command_args) { Write-Host $command $command_args Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru } function InstallPip ($python_home) { $pip_path = $python_home + "\Scripts\pip.exe" $python_path = $python_home + "\python.exe" if (-not(Test-Path $pip_path)) { Write-Host "Installing pip..." $webclient = New-Object System.Net.WebClient $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) Write-Host "Executing:" $python_path $GET_PIP_PATH & $python_path $GET_PIP_PATH } else { Write-Host "pip already installed." } } function DownloadMiniconda ($python_version, $platform_suffix) { if ($python_version -eq "3.4") { $filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe" } else { $filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe" } $url = $MINICONDA_URL + $filename $filepath = Download $filename $url return $filepath } function InstallMiniconda ($python_version, $architecture, $python_home) { Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home if (Test-Path $python_home) { Write-Host $python_home "already exists, skipping." return $false } if ($architecture -eq "32") { $platform_suffix = "x86" } else { $platform_suffix = "x86_64" } $filepath = DownloadMiniconda $python_version $platform_suffix Write-Host "Installing" $filepath "to" $python_home $install_log = $python_home + ".log" $args = "/S /D=$python_home" Write-Host $filepath $args Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru if (Test-Path $python_home) { Write-Host "Python $python_version ($architecture) installation complete" } else { Write-Host "Failed to install Python in $python_home" Get-Content -Path $install_log Exit 1 } } function InstallMinicondaPip ($python_home) { $pip_path = $python_home + "\Scripts\pip.exe" $conda_path = $python_home + "\Scripts\conda.exe" if (-not(Test-Path $pip_path)) { Write-Host "Installing pip..." $args = "install --yes pip" Write-Host $conda_path $args Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru } else { Write-Host "pip already installed." } } function main () { InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON InstallPip $env:PYTHON } main python-coverage-4.5+dfsg.1.orig/ci/run_with_env.cmd0000644000076600000620000000661313146037571020262 0ustar staff:: From: https://github.com/ogrisel/python-appveyor-demo/blob/master/appveyor/run_with_env.cmd :: :: :: To build extensions for 64 bit Python 3, we need to configure environment :: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) :: :: To build extensions for 64 bit Python 2, we need to configure environment :: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) :: :: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific :: environment configurations. :: :: Note: this script needs to be run with the /E:ON and /V:ON flags for the :: cmd interpreter, at least for (SDK v7.0) :: :: More details at: :: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows :: http://stackoverflow.com/a/13751649/163740 :: :: Author: Olivier Grisel :: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ :: :: Notes about batch files for Python people: :: :: Quotes in values are literally part of the values: :: SET FOO="bar" :: FOO is now five characters long: " b a r " :: If you don't want quotes, don't include them on the right-hand side. :: :: The CALL lines at the end of this file look redundant, but if you move them :: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y :: case, I don't know why. @ECHO OFF SET COMMAND_TO_RUN=%* SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf :: Extract the major and minor versions, and allow for the minor version to be :: more than 9. This requires the version number to have two dots in it. SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1% IF "%PYTHON_VERSION:~3,1%" == "." ( SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1% ) ELSE ( SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2% ) :: Based on the Python version, determine what SDK version to use, and whether :: to set the SDK for 64-bit. IF %MAJOR_PYTHON_VERSION% == 2 ( SET WINDOWS_SDK_VERSION="v7.0" SET SET_SDK_64=Y ) ELSE ( IF %MAJOR_PYTHON_VERSION% == 3 ( SET WINDOWS_SDK_VERSION="v7.1" IF %MINOR_PYTHON_VERSION% LEQ 4 ( SET SET_SDK_64=Y ) ELSE ( SET SET_SDK_64=N IF EXIST "%WIN_WDK%" ( :: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ REN "%WIN_WDK%" 0wdf ) ) ) ELSE ( ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" EXIT 1 ) ) IF %PYTHON_ARCH% == 64 ( IF %SET_SDK_64% == Y ( ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture SET DISTUTILS_USE_SDK=1 SET MSSdk=1 "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) ELSE ( ECHO Using default MSVC build environment for 64 bit architecture ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) ) ELSE ( ECHO Using default MSVC build environment for 32 bit architecture ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) python-coverage-4.5+dfsg.1.orig/ci/README.txt0000644000076600000620000000006113146037571016553 0ustar staffFiles to support continuous integration systems. python-coverage-4.5+dfsg.1.orig/ci/download_appveyor.py0000644000076600000620000000576613146037571021204 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Use the Appveyor API to download Windows artifacts.""" import os import os.path import sys import zipfile import requests def make_auth_headers(): """Make the authentication headers needed to use the Appveyor API.""" with open("ci/appveyor.token") as f: token = f.read().strip() headers = { 'Authorization': 'Bearer {0}'.format(token), } return headers def make_url(url, **kwargs): """Build an Appveyor API url.""" return "https://ci.appveyor.com/api" + url.format(**kwargs) def get_project_build(account_project): """Get the details of the latest Appveyor build.""" url = make_url("/projects/{account_project}", account_project=account_project) response = requests.get(url, headers=make_auth_headers()) return response.json() def download_latest_artifacts(account_project): """Download all the artifacts from the latest build.""" build = get_project_build(account_project) jobs = build['build']['jobs'] print("Build {0[build][version]}, {1} jobs: {0[build][message]}".format(build, len(jobs))) for job in jobs: name = job['name'].partition(':')[2].split(',')[0].strip() print(" {0}: {1[status]}, {1[artifactsCount]} artifacts".format(name, job)) url = make_url("/buildjobs/{jobid}/artifacts", jobid=job['jobId']) response = requests.get(url, headers=make_auth_headers()) artifacts = response.json() for artifact in artifacts: is_zip = artifact['type'] == "Zip" filename = artifact['fileName'] print(" {0}, {1} bytes".format(filename, artifact['size'])) url = make_url( "/buildjobs/{jobid}/artifacts/{filename}", jobid=job['jobId'], filename=filename ) download_url(url, filename, make_auth_headers()) if is_zip: unpack_zipfile(filename) os.remove(filename) def ensure_dirs(filename): """Make sure the directories exist for `filename`.""" dirname, _ = os.path.split(filename) if dirname and not os.path.exists(dirname): os.makedirs(dirname) def download_url(url, filename, headers): """Download a file from `url` to `filename`.""" ensure_dirs(filename) response = requests.get(url, headers=headers, stream=True) if response.status_code == 200: with open(filename, 'wb') as f: for chunk in response.iter_content(16*1024): f.write(chunk) def unpack_zipfile(filename): """Unpack a zipfile, using the names in the zip.""" with open(filename, 'rb') as fzip: z = zipfile.ZipFile(fzip) for name in z.namelist(): print(" extracting {0}".format(name)) ensure_dirs(name) z.extract(name) if __name__ == "__main__": download_latest_artifacts(sys.argv[1]) python-coverage-4.5+dfsg.1.orig/LICENSE.txt0000644000076600000620000002367613146037571016326 0ustar staff Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS python-coverage-4.5+dfsg.1.orig/coverage/0000755000076600000620000000000013235414514016253 5ustar staffpython-coverage-4.5+dfsg.1.orig/coverage/control.py0000644000076600000620000013726313231500331020306 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Core control stuff for coverage.py.""" import atexit import inspect import itertools import os import platform import re import sys import time import traceback from coverage import env from coverage.annotate import AnnotateReporter from coverage.backward import string_class, iitems from coverage.collector import Collector from coverage.config import read_coverage_config from coverage.data import CoverageData, CoverageDataFiles from coverage.debug import DebugControl, write_formatted_info from coverage.files import TreeMatcher, FnmatchMatcher from coverage.files import PathAliases, find_python_files, prep_patterns from coverage.files import canonical_filename, set_relative_directory from coverage.files import ModuleMatcher, abs_file from coverage.html import HtmlReporter from coverage.misc import CoverageException, bool_or_none, join_regex from coverage.misc import file_be_gone, isolate_module from coverage.plugin import FileReporter from coverage.plugin_support import Plugins from coverage.python import PythonFileReporter, source_for_file from coverage.results import Analysis, Numbers from coverage.summary import SummaryReporter from coverage.xmlreport import XmlReporter try: from coverage.multiproc import patch_multiprocessing except ImportError: # pragma: only jython # Jython has no multiprocessing module. patch_multiprocessing = None os = isolate_module(os) # Pypy has some unusual stuff in the "stdlib". Consider those locations # when deciding where the stdlib is. These modules are not used for anything, # they are modules importable from the pypy lib directories, so that we can # find those directories. _structseq = _pypy_irc_topic = None if env.PYPY: try: import _structseq except ImportError: pass try: import _pypy_irc_topic except ImportError: pass class Coverage(object): """Programmatic access to coverage.py. To use:: from coverage import Coverage cov = Coverage() cov.start() #.. call your code .. cov.stop() cov.html_report(directory='covhtml') """ def __init__( self, data_file=None, data_suffix=None, cover_pylib=None, auto_data=False, timid=None, branch=None, config_file=True, source=None, omit=None, include=None, debug=None, concurrency=None, ): """ `data_file` is the base name of the data file to use, defaulting to ".coverage". `data_suffix` is appended (with a dot) to `data_file` to create the final file name. If `data_suffix` is simply True, then a suffix is created with the machine and process identity included. `cover_pylib` is a boolean determining whether Python code installed with the Python interpreter is measured. This includes the Python standard library and any packages installed with the interpreter. If `auto_data` is true, then any existing data file will be read when coverage measurement starts, and data will be saved automatically when measurement stops. If `timid` is true, then a slower and simpler trace function will be used. This is important for some environments where manipulation of tracing functions breaks the faster trace function. If `branch` is true, then branch coverage will be measured in addition to the usual statement coverage. `config_file` determines what configuration file to read: * If it is ".coveragerc", it is interpreted as if it were True, for backward compatibility. * If it is a string, it is the name of the file to read. If the file can't be read, it is an error. * If it is True, then a few standard files names are tried (".coveragerc", "setup.cfg", "tox.ini"). It is not an error for these files to not be found. * If it is False, then no configuration file is read. `source` is a list of file paths or package names. Only code located in the trees indicated by the file paths or package names will be measured. `include` and `omit` are lists of file name patterns. Files that match `include` will be measured, files that match `omit` will not. Each will also accept a single string argument. `debug` is a list of strings indicating what debugging information is desired. `concurrency` is a string indicating the concurrency library being used in the measured code. Without this, coverage.py will get incorrect results if these libraries are in use. Valid strings are "greenlet", "eventlet", "gevent", "multiprocessing", or "thread" (the default). This can also be a list of these strings. .. versionadded:: 4.0 The `concurrency` parameter. .. versionadded:: 4.2 The `concurrency` parameter can now be a list of strings. """ # Build our configuration from a number of sources. self.config_file, self.config = read_coverage_config( config_file=config_file, data_file=data_file, cover_pylib=cover_pylib, timid=timid, branch=branch, parallel=bool_or_none(data_suffix), source=source, run_omit=omit, run_include=include, debug=debug, report_omit=omit, report_include=include, concurrency=concurrency, ) # This is injectable by tests. self._debug_file = None self._auto_load = self._auto_save = auto_data self._data_suffix = data_suffix # The matchers for _should_trace. self.source_match = None self.source_pkgs_match = None self.pylib_match = self.cover_match = None self.include_match = self.omit_match = None # Is it ok for no data to be collected? self._warn_no_data = True self._warn_unimported_source = True # A record of all the warnings that have been issued. self._warnings = [] # Other instance attributes, set later. self.omit = self.include = self.source = None self.source_pkgs_unmatched = None self.source_pkgs = None self.data = self.data_files = self.collector = None self.plugins = None self.pylib_paths = self.cover_paths = None self.data_suffix = self.run_suffix = None self._exclude_re = None self.debug = None # State machine variables: # Have we initialized everything? self._inited = False # Have we started collecting and not stopped it? self._started = False # If we have sub-process measurement happening automatically, then we # want any explicit creation of a Coverage object to mean, this process # is already coverage-aware, so don't auto-measure it. By now, the # auto-creation of a Coverage object has already happened. But we can # find it and tell it not to save its data. if not env.METACOV: _prevent_sub_process_measurement() def _init(self): """Set all the initial state. This is called by the public methods to initialize state. This lets us construct a :class:`Coverage` object, then tweak its state before this function is called. """ if self._inited: return self._inited = True # Create and configure the debugging controller. COVERAGE_DEBUG_FILE # is an environment variable, the name of a file to append debug logs # to. if self._debug_file is None: debug_file_name = os.environ.get("COVERAGE_DEBUG_FILE") if debug_file_name: self._debug_file = open(debug_file_name, "a") else: self._debug_file = sys.stderr self.debug = DebugControl(self.config.debug, self._debug_file) # _exclude_re is a dict that maps exclusion list names to compiled regexes. self._exclude_re = {} set_relative_directory() # Load plugins self.plugins = Plugins.load_plugins(self.config.plugins, self.config, self.debug) # Run configuring plugins. for plugin in self.plugins.configurers: # We need an object with set_option and get_option. Either self or # self.config will do. Choosing randomly stops people from doing # other things with those objects, against the public API. Yes, # this is a bit childish. :) plugin.configure([self, self.config][int(time.time()) % 2]) # The source argument can be directories or package names. self.source = [] self.source_pkgs = [] for src in self.config.source or []: if os.path.isdir(src): self.source.append(canonical_filename(src)) else: self.source_pkgs.append(src) self.source_pkgs_unmatched = self.source_pkgs[:] self.omit = prep_patterns(self.config.run_omit) self.include = prep_patterns(self.config.run_include) concurrency = self.config.concurrency or [] if "multiprocessing" in concurrency: if not patch_multiprocessing: raise CoverageException( # pragma: only jython "multiprocessing is not supported on this Python" ) patch_multiprocessing(rcfile=self.config_file) # Multi-processing uses parallel for the subprocesses, so also use # it for the main process. self.config.parallel = True self.collector = Collector( should_trace=self._should_trace, check_include=self._check_include_omit_etc, timid=self.config.timid, branch=self.config.branch, warn=self._warn, concurrency=concurrency, ) # Early warning if we aren't going to be able to support plugins. if self.plugins.file_tracers and not self.collector.supports_plugins: self._warn( "Plugin file tracers (%s) aren't supported with %s" % ( ", ".join( plugin._coverage_plugin_name for plugin in self.plugins.file_tracers ), self.collector.tracer_name(), ) ) for plugin in self.plugins.file_tracers: plugin._coverage_enabled = False # Suffixes are a bit tricky. We want to use the data suffix only when # collecting data, not when combining data. So we save it as # `self.run_suffix` now, and promote it to `self.data_suffix` if we # find that we are collecting data later. if self._data_suffix or self.config.parallel: if not isinstance(self._data_suffix, string_class): # if data_suffix=True, use .machinename.pid.random self._data_suffix = True else: self._data_suffix = None self.data_suffix = None self.run_suffix = self._data_suffix # Create the data file. We do this at construction time so that the # data file will be written into the directory where the process # started rather than wherever the process eventually chdir'd to. self.data = CoverageData(debug=self.debug) self.data_files = CoverageDataFiles( basename=self.config.data_file, warn=self._warn, debug=self.debug, ) # The directories for files considered "installed with the interpreter". self.pylib_paths = set() if not self.config.cover_pylib: # Look at where some standard modules are located. That's the # indication for "installed with the interpreter". In some # environments (virtualenv, for example), these modules may be # spread across a few locations. Look at all the candidate modules # we've imported, and take all the different ones. for m in (atexit, inspect, os, platform, _pypy_irc_topic, re, _structseq, traceback): if m is not None and hasattr(m, "__file__"): self.pylib_paths.add(self._canonical_path(m, directory=True)) if _structseq and not hasattr(_structseq, '__file__'): # PyPy 2.4 has no __file__ in the builtin modules, but the code # objects still have the file names. So dig into one to find # the path to exclude. structseq_new = _structseq.structseq_new try: structseq_file = structseq_new.func_code.co_filename except AttributeError: structseq_file = structseq_new.__code__.co_filename self.pylib_paths.add(self._canonical_path(structseq_file)) # To avoid tracing the coverage.py code itself, we skip anything # located where we are. self.cover_paths = [self._canonical_path(__file__, directory=True)] if env.TESTING: # Don't include our own test code. self.cover_paths.append(os.path.join(self.cover_paths[0], "tests")) # When testing, we use PyContracts, which should be considered # part of coverage.py, and it uses six. Exclude those directories # just as we exclude ourselves. import contracts import six for mod in [contracts, six]: self.cover_paths.append(self._canonical_path(mod)) # Set the reporting precision. Numbers.set_precision(self.config.precision) atexit.register(self._atexit) # Create the matchers we need for _should_trace if self.source or self.source_pkgs: self.source_match = TreeMatcher(self.source) self.source_pkgs_match = ModuleMatcher(self.source_pkgs) else: if self.cover_paths: self.cover_match = TreeMatcher(self.cover_paths) if self.pylib_paths: self.pylib_match = TreeMatcher(self.pylib_paths) if self.include: self.include_match = FnmatchMatcher(self.include) if self.omit: self.omit_match = FnmatchMatcher(self.omit) # The user may want to debug things, show info if desired. self._write_startup_debug() def _write_startup_debug(self): """Write out debug info at startup if needed.""" wrote_any = False with self.debug.without_callers(): if self.debug.should('config'): config_info = sorted(self.config.__dict__.items()) write_formatted_info(self.debug, "config", config_info) wrote_any = True if self.debug.should('sys'): write_formatted_info(self.debug, "sys", self.sys_info()) for plugin in self.plugins: header = "sys: " + plugin._coverage_plugin_name info = plugin.sys_info() write_formatted_info(self.debug, header, info) wrote_any = True if wrote_any: write_formatted_info(self.debug, "end", ()) def _canonical_path(self, morf, directory=False): """Return the canonical path of the module or file `morf`. If the module is a package, then return its directory. If it is a module, then return its file, unless `directory` is True, in which case return its enclosing directory. """ morf_path = PythonFileReporter(morf, self).filename if morf_path.endswith("__init__.py") or directory: morf_path = os.path.split(morf_path)[0] return morf_path def _name_for_module(self, module_globals, filename): """Get the name of the module for a set of globals and file name. For configurability's sake, we allow __main__ modules to be matched by their importable name. If loaded via runpy (aka -m), we can usually recover the "original" full dotted module name, otherwise, we resort to interpreting the file name to get the module's name. In the case that the module name can't be determined, None is returned. """ if module_globals is None: # pragma: only ironpython # IronPython doesn't provide globals: https://github.com/IronLanguages/main/issues/1296 module_globals = {} dunder_name = module_globals.get('__name__', None) if isinstance(dunder_name, str) and dunder_name != '__main__': # This is the usual case: an imported module. return dunder_name loader = module_globals.get('__loader__', None) for attrname in ('fullname', 'name'): # attribute renamed in py3.2 if hasattr(loader, attrname): fullname = getattr(loader, attrname) else: continue if isinstance(fullname, str) and fullname != '__main__': # Module loaded via: runpy -m return fullname # Script as first argument to Python command line. inspectedname = inspect.getmodulename(filename) if inspectedname is not None: return inspectedname else: return dunder_name def _should_trace_internal(self, filename, frame): """Decide whether to trace execution in `filename`, with a reason. This function is called from the trace function. As each new file name is encountered, this function determines whether it is traced or not. Returns a FileDisposition object. """ original_filename = filename disp = _disposition_init(self.collector.file_disposition_class, filename) def nope(disp, reason): """Simple helper to make it easy to return NO.""" disp.trace = False disp.reason = reason return disp # Compiled Python files have two file names: frame.f_code.co_filename is # the file name at the time the .pyc was compiled. The second name is # __file__, which is where the .pyc was actually loaded from. Since # .pyc files can be moved after compilation (for example, by being # installed), we look for __file__ in the frame and prefer it to the # co_filename value. dunder_file = frame.f_globals and frame.f_globals.get('__file__') if dunder_file: filename = source_for_file(dunder_file) if original_filename and not original_filename.startswith('<'): orig = os.path.basename(original_filename) if orig != os.path.basename(filename): # Files shouldn't be renamed when moved. This happens when # exec'ing code. If it seems like something is wrong with # the frame's file name, then just use the original. filename = original_filename if not filename: # Empty string is pretty useless. return nope(disp, "empty string isn't a file name") if filename.startswith('memory:'): return nope(disp, "memory isn't traceable") if filename.startswith('<'): # Lots of non-file execution is represented with artificial # file names like "", "", or # "". Don't ever trace these executions, since we # can't do anything with the data later anyway. return nope(disp, "not a real file name") # pyexpat does a dumb thing, calling the trace function explicitly from # C code with a C file name. if re.search(r"[/\\]Modules[/\\]pyexpat.c", filename): return nope(disp, "pyexpat lies about itself") # Jython reports the .class file to the tracer, use the source file. if filename.endswith("$py.class"): filename = filename[:-9] + ".py" canonical = canonical_filename(filename) disp.canonical_filename = canonical # Try the plugins, see if they have an opinion about the file. plugin = None for plugin in self.plugins.file_tracers: if not plugin._coverage_enabled: continue try: file_tracer = plugin.file_tracer(canonical) if file_tracer is not None: file_tracer._coverage_plugin = plugin disp.trace = True disp.file_tracer = file_tracer if file_tracer.has_dynamic_source_filename(): disp.has_dynamic_filename = True else: disp.source_filename = canonical_filename( file_tracer.source_filename() ) break except Exception: self._warn( "Disabling plug-in %r due to an exception:" % ( plugin._coverage_plugin_name ) ) traceback.print_exc() plugin._coverage_enabled = False continue else: # No plugin wanted it: it's Python. disp.trace = True disp.source_filename = canonical if not disp.has_dynamic_filename: if not disp.source_filename: raise CoverageException( "Plugin %r didn't set source_filename for %r" % (plugin, disp.original_filename) ) reason = self._check_include_omit_etc_internal( disp.source_filename, frame, ) if reason: nope(disp, reason) return disp def _check_include_omit_etc_internal(self, filename, frame): """Check a file name against the include, omit, etc, rules. Returns a string or None. String means, don't trace, and is the reason why. None means no reason found to not trace. """ modulename = self._name_for_module(frame.f_globals, filename) # If the user specified source or include, then that's authoritative # about the outer bound of what to measure and we don't have to apply # any canned exclusions. If they didn't, then we have to exclude the # stdlib and coverage.py directories. if self.source_match: if self.source_pkgs_match.match(modulename): if modulename in self.source_pkgs_unmatched: self.source_pkgs_unmatched.remove(modulename) return None # There's no reason to skip this file. if not self.source_match.match(filename): return "falls outside the --source trees" elif self.include_match: if not self.include_match.match(filename): return "falls outside the --include trees" else: # If we aren't supposed to trace installed code, then check if this # is near the Python standard library and skip it if so. if self.pylib_match and self.pylib_match.match(filename): return "is in the stdlib" # We exclude the coverage.py code itself, since a little of it # will be measured otherwise. if self.cover_match and self.cover_match.match(filename): return "is part of coverage.py" # Check the file against the omit pattern. if self.omit_match and self.omit_match.match(filename): return "is inside an --omit pattern" # No reason found to skip this file. return None def _should_trace(self, filename, frame): """Decide whether to trace execution in `filename`. Calls `_should_trace_internal`, and returns the FileDisposition. """ disp = self._should_trace_internal(filename, frame) if self.debug.should('trace'): self.debug.write(_disposition_debug_msg(disp)) return disp def _check_include_omit_etc(self, filename, frame): """Check a file name against the include/omit/etc, rules, verbosely. Returns a boolean: True if the file should be traced, False if not. """ reason = self._check_include_omit_etc_internal(filename, frame) if self.debug.should('trace'): if not reason: msg = "Including %r" % (filename,) else: msg = "Not including %r: %s" % (filename, reason) self.debug.write(msg) return not reason def _warn(self, msg, slug=None): """Use `msg` as a warning. For warning suppression, use `slug` as the shorthand. """ if slug in self.config.disable_warnings: # Don't issue the warning return self._warnings.append(msg) if slug: msg = "%s (%s)" % (msg, slug) if self.debug.should('pid'): msg = "[%d] %s" % (os.getpid(), msg) sys.stderr.write("Coverage.py warning: %s\n" % msg) def get_option(self, option_name): """Get an option from the configuration. `option_name` is a colon-separated string indicating the section and option name. For example, the ``branch`` option in the ``[run]`` section of the config file would be indicated with `"run:branch"`. Returns the value of the option. .. versionadded:: 4.0 """ return self.config.get_option(option_name) def set_option(self, option_name, value): """Set an option in the configuration. `option_name` is a colon-separated string indicating the section and option name. For example, the ``branch`` option in the ``[run]`` section of the config file would be indicated with ``"run:branch"``. `value` is the new value for the option. This should be an appropriate Python value. For example, use True for booleans, not the string ``"True"``. As an example, calling:: cov.set_option("run:branch", True) has the same effect as this configuration file:: [run] branch = True .. versionadded:: 4.0 """ self.config.set_option(option_name, value) def use_cache(self, usecache): """Obsolete method.""" self._init() if not usecache: self._warn("use_cache(False) is no longer supported.") def load(self): """Load previously-collected coverage data from the data file.""" self._init() self.collector.reset() self.data_files.read(self.data) def start(self): """Start measuring code coverage. Coverage measurement only occurs in functions called after :meth:`start` is invoked. Statements in the same scope as :meth:`start` won't be measured. Once you invoke :meth:`start`, you must also call :meth:`stop` eventually, or your process might not shut down cleanly. """ self._init() if self.include: if self.source or self.source_pkgs: self._warn("--include is ignored because --source is set", slug="include-ignored") if self.run_suffix: # Calling start() means we're running code, so use the run_suffix # as the data_suffix when we eventually save the data. self.data_suffix = self.run_suffix if self._auto_load: self.load() self.collector.start() self._started = True def stop(self): """Stop measuring code coverage.""" if self._started: self.collector.stop() self._started = False def _atexit(self): """Clean up on process shutdown.""" if self.debug.should("process"): self.debug.write("atexit: {0!r}".format(self)) if self._started: self.stop() if self._auto_save: self.save() def erase(self): """Erase previously-collected coverage data. This removes the in-memory data collected in this session as well as discarding the data file. """ self._init() self.collector.reset() self.data.erase() self.data_files.erase(parallel=self.config.parallel) def clear_exclude(self, which='exclude'): """Clear the exclude list.""" self._init() setattr(self.config, which + "_list", []) self._exclude_regex_stale() def exclude(self, regex, which='exclude'): """Exclude source lines from execution consideration. A number of lists of regular expressions are maintained. Each list selects lines that are treated differently during reporting. `which` determines which list is modified. The "exclude" list selects lines that are not considered executable at all. The "partial" list indicates lines with branches that are not taken. `regex` is a regular expression. The regex is added to the specified list. If any of the regexes in the list is found in a line, the line is marked for special treatment during reporting. """ self._init() excl_list = getattr(self.config, which + "_list") excl_list.append(regex) self._exclude_regex_stale() def _exclude_regex_stale(self): """Drop all the compiled exclusion regexes, a list was modified.""" self._exclude_re.clear() def _exclude_regex(self, which): """Return a compiled regex for the given exclusion list.""" if which not in self._exclude_re: excl_list = getattr(self.config, which + "_list") self._exclude_re[which] = join_regex(excl_list) return self._exclude_re[which] def get_exclude_list(self, which='exclude'): """Return a list of excluded regex patterns. `which` indicates which list is desired. See :meth:`exclude` for the lists that are available, and their meaning. """ self._init() return getattr(self.config, which + "_list") def save(self): """Save the collected coverage data to the data file.""" self._init() self.get_data() self.data_files.write(self.data, suffix=self.data_suffix) def combine(self, data_paths=None, strict=False): """Combine together a number of similarly-named coverage data files. All coverage data files whose name starts with `data_file` (from the coverage() constructor) will be read, and combined together into the current measurements. `data_paths` is a list of files or directories from which data should be combined. If no list is passed, then the data files from the directory indicated by the current data file (probably the current directory) will be combined. If `strict` is true, then it is an error to attempt to combine when there are no data files to combine. .. versionadded:: 4.0 The `data_paths` parameter. .. versionadded:: 4.3 The `strict` parameter. """ self._init() self.get_data() aliases = None if self.config.paths: aliases = PathAliases() for paths in self.config.paths.values(): result = paths[0] for pattern in paths[1:]: aliases.add(pattern, result) self.data_files.combine_parallel_data( self.data, aliases=aliases, data_paths=data_paths, strict=strict, ) def get_data(self): """Get the collected data. Also warn about various problems collecting data. Returns a :class:`coverage.CoverageData`, the collected coverage data. .. versionadded:: 4.0 """ self._init() if self.collector.save_data(self.data): self._post_save_work() return self.data def _post_save_work(self): """After saving data, look for warnings, post-work, etc. Warn about things that should have happened but didn't. Look for unexecuted files. """ # If there are still entries in the source_pkgs_unmatched list, # then we never encountered those packages. if self._warn_unimported_source: for pkg in self.source_pkgs_unmatched: self._warn_about_unmeasured_code(pkg) # Find out if we got any data. if not self.data and self._warn_no_data: self._warn("No data was collected.", slug="no-data-collected") # Find files that were never executed at all. for pkg in self.source_pkgs: if (not pkg in sys.modules or not hasattr(sys.modules[pkg], '__file__') or not os.path.exists(sys.modules[pkg].__file__)): continue pkg_file = source_for_file(sys.modules[pkg].__file__) self._find_unexecuted_files(self._canonical_path(pkg_file)) for src in self.source: self._find_unexecuted_files(src) if self.config.note: self.data.add_run_info(note=self.config.note) def _warn_about_unmeasured_code(self, pkg): """Warn about a package or module that we never traced. `pkg` is a string, the name of the package or module. """ mod = sys.modules.get(pkg) if mod is None: self._warn("Module %s was never imported." % pkg, slug="module-not-imported") return is_namespace = hasattr(mod, '__path__') and not hasattr(mod, '__file__') has_file = hasattr(mod, '__file__') and os.path.exists(mod.__file__) if is_namespace: # A namespace package. It's OK for this not to have been traced, # since there is no code directly in it. return if not has_file: self._warn("Module %s has no Python source." % pkg, slug="module-not-python") return # The module was in sys.modules, and seems like a module with code, but # we never measured it. I guess that means it was imported before # coverage even started. self._warn( "Module %s was previously imported, but not measured" % pkg, slug="module-not-measured", ) def _find_plugin_files(self, src_dir): """Get executable files from the plugins.""" for plugin in self.plugins: for x_file in plugin.find_executable_files(src_dir): yield x_file, plugin._coverage_plugin_name def _find_unexecuted_files(self, src_dir): """Find unexecuted files in `src_dir`. Search for files in `src_dir` that are probably importable, and add them as unexecuted files in `self.data`. """ py_files = ((py_file, None) for py_file in find_python_files(src_dir)) plugin_files = self._find_plugin_files(src_dir) for file_path, plugin_name in itertools.chain(py_files, plugin_files): file_path = canonical_filename(file_path) if self.omit_match and self.omit_match.match(file_path): # Turns out this file was omitted, so don't pull it back # in as unexecuted. continue self.data.touch_file(file_path, plugin_name) # Backward compatibility with version 1. def analysis(self, morf): """Like `analysis2` but doesn't return excluded line numbers.""" f, s, _, m, mf = self.analysis2(morf) return f, s, m, mf def analysis2(self, morf): """Analyze a module. `morf` is a module or a file name. It will be analyzed to determine its coverage statistics. The return value is a 5-tuple: * The file name for the module. * A list of line numbers of executable statements. * A list of line numbers of excluded statements. * A list of line numbers of statements not run (missing from execution). * A readable formatted string of the missing line numbers. The analysis uses the source file itself and the current measured coverage data. """ self._init() analysis = self._analyze(morf) return ( analysis.filename, sorted(analysis.statements), sorted(analysis.excluded), sorted(analysis.missing), analysis.missing_formatted(), ) def _analyze(self, it): """Analyze a single morf or code unit. Returns an `Analysis` object. """ self.get_data() if not isinstance(it, FileReporter): it = self._get_file_reporter(it) return Analysis(self.data, it) def _get_file_reporter(self, morf): """Get a FileReporter for a module or file name.""" plugin = None file_reporter = "python" if isinstance(morf, string_class): abs_morf = abs_file(morf) plugin_name = self.data.file_tracer(abs_morf) if plugin_name: plugin = self.plugins.get(plugin_name) if plugin: file_reporter = plugin.file_reporter(abs_morf) if file_reporter is None: raise CoverageException( "Plugin %r did not provide a file reporter for %r." % ( plugin._coverage_plugin_name, morf ) ) if file_reporter == "python": file_reporter = PythonFileReporter(morf, self) return file_reporter def _get_file_reporters(self, morfs=None): """Get a list of FileReporters for a list of modules or file names. For each module or file name in `morfs`, find a FileReporter. Return the list of FileReporters. If `morfs` is a single module or file name, this returns a list of one FileReporter. If `morfs` is empty or None, then the list of all files measured is used to find the FileReporters. """ if not morfs: morfs = self.data.measured_files() # Be sure we have a list. if not isinstance(morfs, (list, tuple)): morfs = [morfs] file_reporters = [] for morf in morfs: file_reporter = self._get_file_reporter(morf) file_reporters.append(file_reporter) return file_reporters def report( self, morfs=None, show_missing=None, ignore_errors=None, file=None, # pylint: disable=redefined-builtin omit=None, include=None, skip_covered=None, ): """Write a summary report to `file`. Each module in `morfs` is listed, with counts of statements, executed statements, missing statements, and a list of lines missed. `include` is a list of file name patterns. Files that match will be included in the report. Files matching `omit` will not be included in the report. If `skip_covered` is True, don't report on files with 100% coverage. Returns a float, the total percentage covered. """ self.get_data() self.config.from_args( ignore_errors=ignore_errors, report_omit=omit, report_include=include, show_missing=show_missing, skip_covered=skip_covered, ) reporter = SummaryReporter(self, self.config) return reporter.report(morfs, outfile=file) def annotate( self, morfs=None, directory=None, ignore_errors=None, omit=None, include=None, ): """Annotate a list of modules. Each module in `morfs` is annotated. The source is written to a new file, named with a ",cover" suffix, with each line prefixed with a marker to indicate the coverage of the line. Covered lines have ">", excluded lines have "-", and missing lines have "!". See :meth:`report` for other arguments. """ self.get_data() self.config.from_args( ignore_errors=ignore_errors, report_omit=omit, report_include=include ) reporter = AnnotateReporter(self, self.config) reporter.report(morfs, directory=directory) def html_report(self, morfs=None, directory=None, ignore_errors=None, omit=None, include=None, extra_css=None, title=None, skip_covered=None): """Generate an HTML report. The HTML is written to `directory`. The file "index.html" is the overview starting point, with links to more detailed pages for individual modules. `extra_css` is a path to a file of other CSS to apply on the page. It will be copied into the HTML directory. `title` is a text string (not HTML) to use as the title of the HTML report. See :meth:`report` for other arguments. Returns a float, the total percentage covered. """ self.get_data() self.config.from_args( ignore_errors=ignore_errors, report_omit=omit, report_include=include, html_dir=directory, extra_css=extra_css, html_title=title, skip_covered=skip_covered, ) reporter = HtmlReporter(self, self.config) return reporter.report(morfs) def xml_report( self, morfs=None, outfile=None, ignore_errors=None, omit=None, include=None, ): """Generate an XML report of coverage results. The report is compatible with Cobertura reports. Each module in `morfs` is included in the report. `outfile` is the path to write the file to, "-" will write to stdout. See :meth:`report` for other arguments. Returns a float, the total percentage covered. """ self.get_data() self.config.from_args( ignore_errors=ignore_errors, report_omit=omit, report_include=include, xml_output=outfile, ) file_to_close = None delete_file = False if self.config.xml_output: if self.config.xml_output == '-': outfile = sys.stdout else: # Ensure that the output directory is created; done here # because this report pre-opens the output file. # HTMLReport does this using the Report plumbing because # its task is more complex, being multiple files. output_dir = os.path.dirname(self.config.xml_output) if output_dir and not os.path.isdir(output_dir): os.makedirs(output_dir) open_kwargs = {} if env.PY3: open_kwargs['encoding'] = 'utf8' outfile = open(self.config.xml_output, "w", **open_kwargs) file_to_close = outfile try: reporter = XmlReporter(self, self.config) return reporter.report(morfs, outfile=outfile) except CoverageException: delete_file = True raise finally: if file_to_close: file_to_close.close() if delete_file: file_be_gone(self.config.xml_output) def sys_info(self): """Return a list of (key, value) pairs showing internal information.""" import coverage as covmod self._init() ft_plugins = [] for ft in self.plugins.file_tracers: ft_name = ft._coverage_plugin_name if not ft._coverage_enabled: ft_name += " (disabled)" ft_plugins.append(ft_name) info = [ ('version', covmod.__version__), ('coverage', covmod.__file__), ('cover_paths', self.cover_paths), ('pylib_paths', self.pylib_paths), ('tracer', self.collector.tracer_name()), ('plugins.file_tracers', ft_plugins), ('config_files', self.config.attempted_config_files), ('configs_read', self.config.config_files), ('data_path', self.data_files.filename), ('python', sys.version.replace('\n', '')), ('platform', platform.platform()), ('implementation', platform.python_implementation()), ('executable', sys.executable), ('cwd', os.getcwd()), ('path', sys.path), ('environment', sorted( ("%s = %s" % (k, v)) for k, v in iitems(os.environ) if k.startswith(("COV", "PY")) )), ('command_line', " ".join(getattr(sys, 'argv', ['???']))), ] matcher_names = [ 'source_match', 'source_pkgs_match', 'include_match', 'omit_match', 'cover_match', 'pylib_match', ] for matcher_name in matcher_names: matcher = getattr(self, matcher_name) if matcher: matcher_info = matcher.info() else: matcher_info = '-none-' info.append((matcher_name, matcher_info)) return info # FileDisposition "methods": FileDisposition is a pure value object, so it can # be implemented in either C or Python. Acting on them is done with these # functions. def _disposition_init(cls, original_filename): """Construct and initialize a new FileDisposition object.""" disp = cls() disp.original_filename = original_filename disp.canonical_filename = original_filename disp.source_filename = None disp.trace = False disp.reason = "" disp.file_tracer = None disp.has_dynamic_filename = False return disp def _disposition_debug_msg(disp): """Make a nice debug message of what the FileDisposition is doing.""" if disp.trace: msg = "Tracing %r" % (disp.original_filename,) if disp.file_tracer: msg += ": will be traced by %r" % disp.file_tracer else: msg = "Not tracing %r: %s" % (disp.original_filename, disp.reason) return msg def process_startup(): """Call this at Python start-up to perhaps measure coverage. If the environment variable COVERAGE_PROCESS_START is defined, coverage measurement is started. The value of the variable is the config file to use. There are two ways to configure your Python installation to invoke this function when Python starts: #. Create or append to sitecustomize.py to add these lines:: import coverage coverage.process_startup() #. Create a .pth file in your Python installation containing:: import coverage; coverage.process_startup() Returns the :class:`Coverage` instance that was started, or None if it was not started by this call. """ cps = os.environ.get("COVERAGE_PROCESS_START") if not cps: # No request for coverage, nothing to do. return None # This function can be called more than once in a process. This happens # because some virtualenv configurations make the same directory visible # twice in sys.path. This means that the .pth file will be found twice, # and executed twice, executing this function twice. We set a global # flag (an attribute on this function) to indicate that coverage.py has # already been started, so we can avoid doing it twice. # # https://bitbucket.org/ned/coveragepy/issue/340/keyerror-subpy has more # details. if hasattr(process_startup, "coverage"): # We've annotated this function before, so we must have already # started coverage.py in this process. Nothing to do. return None cov = Coverage(config_file=cps) process_startup.coverage = cov cov.start() cov._warn_no_data = False cov._warn_unimported_source = False cov._auto_save = True return cov def _prevent_sub_process_measurement(): """Stop any subprocess auto-measurement from writing data.""" auto_created_coverage = getattr(process_startup, "coverage", None) if auto_created_coverage is not None: auto_created_coverage._auto_save = False python-coverage-4.5+dfsg.1.orig/coverage/templite.py0000644000076600000620000002337613146037571020470 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """A simple Python template renderer, for a nano-subset of Django syntax. For a detailed discussion of this code, see this chapter from 500 Lines: http://aosabook.org/en/500L/a-template-engine.html """ # Coincidentally named the same as http://code.activestate.com/recipes/496702/ import re from coverage import env class TempliteSyntaxError(ValueError): """Raised when a template has a syntax error.""" pass class TempliteValueError(ValueError): """Raised when an expression won't evaluate in a template.""" pass class CodeBuilder(object): """Build source code conveniently.""" def __init__(self, indent=0): self.code = [] self.indent_level = indent def __str__(self): return "".join(str(c) for c in self.code) def add_line(self, line): """Add a line of source to the code. Indentation and newline will be added for you, don't provide them. """ self.code.extend([" " * self.indent_level, line, "\n"]) def add_section(self): """Add a section, a sub-CodeBuilder.""" section = CodeBuilder(self.indent_level) self.code.append(section) return section INDENT_STEP = 4 # PEP8 says so! def indent(self): """Increase the current indent for following lines.""" self.indent_level += self.INDENT_STEP def dedent(self): """Decrease the current indent for following lines.""" self.indent_level -= self.INDENT_STEP def get_globals(self): """Execute the code, and return a dict of globals it defines.""" # A check that the caller really finished all the blocks they started. assert self.indent_level == 0 # Get the Python source as a single string. python_source = str(self) # Execute the source, defining globals, and return them. global_namespace = {} exec(python_source, global_namespace) return global_namespace class Templite(object): """A simple template renderer, for a nano-subset of Django syntax. Supported constructs are extended variable access:: {{var.modifier.modifier|filter|filter}} loops:: {% for var in list %}...{% endfor %} and ifs:: {% if var %}...{% endif %} Comments are within curly-hash markers:: {# This will be ignored #} Any of these constructs can have a hypen at the end (`-}}`, `-%}`, `-#}`), which will collapse the whitespace following the tag. Construct a Templite with the template text, then use `render` against a dictionary context to create a finished string:: templite = Templite('''

Hello {{name|upper}}!

{% for topic in topics %}

You are interested in {{topic}}.

{% endif %} ''', {'upper': str.upper}, ) text = templite.render({ 'name': "Ned", 'topics': ['Python', 'Geometry', 'Juggling'], }) """ def __init__(self, text, *contexts): """Construct a Templite with the given `text`. `contexts` are dictionaries of values to use for future renderings. These are good for filters and global values. """ self.context = {} for context in contexts: self.context.update(context) self.all_vars = set() self.loop_vars = set() # We construct a function in source form, then compile it and hold onto # it, and execute it to render the template. code = CodeBuilder() code.add_line("def render_function(context, do_dots):") code.indent() vars_code = code.add_section() code.add_line("result = []") code.add_line("append_result = result.append") code.add_line("extend_result = result.extend") if env.PY2: code.add_line("to_str = unicode") else: code.add_line("to_str = str") buffered = [] def flush_output(): """Force `buffered` to the code builder.""" if len(buffered) == 1: code.add_line("append_result(%s)" % buffered[0]) elif len(buffered) > 1: code.add_line("extend_result([%s])" % ", ".join(buffered)) del buffered[:] ops_stack = [] # Split the text to form a list of tokens. tokens = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text) squash = False for token in tokens: if token.startswith('{'): start, end = 2, -2 squash = (token[-3] == '-') if squash: end = -3 if token.startswith('{#'): # Comment: ignore it and move on. continue elif token.startswith('{{'): # An expression to evaluate. expr = self._expr_code(token[start:end].strip()) buffered.append("to_str(%s)" % expr) else: # token.startswith('{%') # Action tag: split into words and parse further. flush_output() words = token[start:end].strip().split() if words[0] == 'if': # An if statement: evaluate the expression to determine if. if len(words) != 2: self._syntax_error("Don't understand if", token) ops_stack.append('if') code.add_line("if %s:" % self._expr_code(words[1])) code.indent() elif words[0] == 'for': # A loop: iterate over expression result. if len(words) != 4 or words[2] != 'in': self._syntax_error("Don't understand for", token) ops_stack.append('for') self._variable(words[1], self.loop_vars) code.add_line( "for c_%s in %s:" % ( words[1], self._expr_code(words[3]) ) ) code.indent() elif words[0].startswith('end'): # Endsomething. Pop the ops stack. if len(words) != 1: self._syntax_error("Don't understand end", token) end_what = words[0][3:] if not ops_stack: self._syntax_error("Too many ends", token) start_what = ops_stack.pop() if start_what != end_what: self._syntax_error("Mismatched end tag", end_what) code.dedent() else: self._syntax_error("Don't understand tag", words[0]) else: # Literal content. If it isn't empty, output it. if squash: token = token.lstrip() if token: buffered.append(repr(token)) if ops_stack: self._syntax_error("Unmatched action tag", ops_stack[-1]) flush_output() for var_name in self.all_vars - self.loop_vars: vars_code.add_line("c_%s = context[%r]" % (var_name, var_name)) code.add_line('return "".join(result)') code.dedent() self._render_function = code.get_globals()['render_function'] def _expr_code(self, expr): """Generate a Python expression for `expr`.""" if "|" in expr: pipes = expr.split("|") code = self._expr_code(pipes[0]) for func in pipes[1:]: self._variable(func, self.all_vars) code = "c_%s(%s)" % (func, code) elif "." in expr: dots = expr.split(".") code = self._expr_code(dots[0]) args = ", ".join(repr(d) for d in dots[1:]) code = "do_dots(%s, %s)" % (code, args) else: self._variable(expr, self.all_vars) code = "c_%s" % expr return code def _syntax_error(self, msg, thing): """Raise a syntax error using `msg`, and showing `thing`.""" raise TempliteSyntaxError("%s: %r" % (msg, thing)) def _variable(self, name, vars_set): """Track that `name` is used as a variable. Adds the name to `vars_set`, a set of variable names. Raises an syntax error if `name` is not a valid name. """ if not re.match(r"[_a-zA-Z][_a-zA-Z0-9]*$", name): self._syntax_error("Not a valid name", name) vars_set.add(name) def render(self, context=None): """Render this template by applying it to `context`. `context` is a dictionary of values to use in this rendering. """ # Make the complete context we'll use. render_context = dict(self.context) if context: render_context.update(context) return self._render_function(render_context, self._do_dots) def _do_dots(self, value, *dots): """Evaluate dotted expressions at run-time.""" for dot in dots: try: value = getattr(value, dot) except AttributeError: try: value = value[dot] except (TypeError, KeyError): raise TempliteValueError( "Couldn't evaluate %r.%s" % (value, dot) ) if callable(value): value = value() return value python-coverage-4.5+dfsg.1.orig/coverage/plugin_support.py0000644000076600000620000002002513220621161021706 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Support for plugins.""" import os import os.path import sys from coverage.misc import CoverageException, isolate_module from coverage.plugin import CoveragePlugin, FileTracer, FileReporter os = isolate_module(os) class Plugins(object): """The currently loaded collection of coverage.py plugins.""" def __init__(self): self.order = [] self.names = {} self.file_tracers = [] self.configurers = [] self.current_module = None self.debug = None @classmethod def load_plugins(cls, modules, config, debug=None): """Load plugins from `modules`. Returns a Plugins object with the loaded and configured plugins. """ plugins = cls() plugins.debug = debug for module in modules: plugins.current_module = module __import__(module) mod = sys.modules[module] coverage_init = getattr(mod, "coverage_init", None) if not coverage_init: raise CoverageException( "Plugin module %r didn't define a coverage_init function" % module ) options = config.get_plugin_options(module) coverage_init(plugins, options) plugins.current_module = None return plugins def add_file_tracer(self, plugin): """Add a file tracer plugin. `plugin` is an instance of a third-party plugin class. It must implement the :meth:`CoveragePlugin.file_tracer` method. """ self._add_plugin(plugin, self.file_tracers) def add_configurer(self, plugin): """Add a configuring plugin. `plugin` is an instance of a third-party plugin class. It must implement the :meth:`CoveragePlugin.configure` method. """ self._add_plugin(plugin, self.configurers) def add_noop(self, plugin): """Add a plugin that does nothing. This is only useful for testing the plugin support. """ self._add_plugin(plugin, None) def _add_plugin(self, plugin, specialized): """Add a plugin object. `plugin` is a :class:`CoveragePlugin` instance to add. `specialized` is a list to append the plugin to. """ plugin_name = "%s.%s" % (self.current_module, plugin.__class__.__name__) if self.debug and self.debug.should('plugin'): self.debug.write("Loaded plugin %r: %r" % (self.current_module, plugin)) labelled = LabelledDebug("plugin %r" % (self.current_module,), self.debug) plugin = DebugPluginWrapper(plugin, labelled) # pylint: disable=attribute-defined-outside-init plugin._coverage_plugin_name = plugin_name plugin._coverage_enabled = True self.order.append(plugin) self.names[plugin_name] = plugin if specialized is not None: specialized.append(plugin) def __nonzero__(self): return bool(self.order) __bool__ = __nonzero__ def __iter__(self): return iter(self.order) def get(self, plugin_name): """Return a plugin by name.""" return self.names[plugin_name] class LabelledDebug(object): """A Debug writer, but with labels for prepending to the messages.""" def __init__(self, label, debug, prev_labels=()): self.labels = list(prev_labels) + [label] self.debug = debug def add_label(self, label): """Add a label to the writer, and return a new `LabelledDebug`.""" return LabelledDebug(label, self.debug, self.labels) def message_prefix(self): """The prefix to use on messages, combining the labels.""" prefixes = self.labels + [''] return ":\n".join(" "*i+label for i, label in enumerate(prefixes)) def write(self, message): """Write `message`, but with the labels prepended.""" self.debug.write("%s%s" % (self.message_prefix(), message)) class DebugPluginWrapper(CoveragePlugin): """Wrap a plugin, and use debug to report on what it's doing.""" def __init__(self, plugin, debug): super(DebugPluginWrapper, self).__init__() self.plugin = plugin self.debug = debug def file_tracer(self, filename): tracer = self.plugin.file_tracer(filename) self.debug.write("file_tracer(%r) --> %r" % (filename, tracer)) if tracer: debug = self.debug.add_label("file %r" % (filename,)) tracer = DebugFileTracerWrapper(tracer, debug) return tracer def file_reporter(self, filename): reporter = self.plugin.file_reporter(filename) self.debug.write("file_reporter(%r) --> %r" % (filename, reporter)) if reporter: debug = self.debug.add_label("file %r" % (filename,)) reporter = DebugFileReporterWrapper(filename, reporter, debug) return reporter def sys_info(self): return self.plugin.sys_info() class DebugFileTracerWrapper(FileTracer): """A debugging `FileTracer`.""" def __init__(self, tracer, debug): self.tracer = tracer self.debug = debug def _show_frame(self, frame): """A short string identifying a frame, for debug messages.""" return "%s@%d" % ( os.path.basename(frame.f_code.co_filename), frame.f_lineno, ) def source_filename(self): sfilename = self.tracer.source_filename() self.debug.write("source_filename() --> %r" % (sfilename,)) return sfilename def has_dynamic_source_filename(self): has = self.tracer.has_dynamic_source_filename() self.debug.write("has_dynamic_source_filename() --> %r" % (has,)) return has def dynamic_source_filename(self, filename, frame): dyn = self.tracer.dynamic_source_filename(filename, frame) self.debug.write("dynamic_source_filename(%r, %s) --> %r" % ( filename, self._show_frame(frame), dyn, )) return dyn def line_number_range(self, frame): pair = self.tracer.line_number_range(frame) self.debug.write("line_number_range(%s) --> %r" % (self._show_frame(frame), pair)) return pair class DebugFileReporterWrapper(FileReporter): """A debugging `FileReporter`.""" def __init__(self, filename, reporter, debug): super(DebugFileReporterWrapper, self).__init__(filename) self.reporter = reporter self.debug = debug def relative_filename(self): ret = self.reporter.relative_filename() self.debug.write("relative_filename() --> %r" % (ret,)) return ret def lines(self): ret = self.reporter.lines() self.debug.write("lines() --> %r" % (ret,)) return ret def excluded_lines(self): ret = self.reporter.excluded_lines() self.debug.write("excluded_lines() --> %r" % (ret,)) return ret def translate_lines(self, lines): ret = self.reporter.translate_lines(lines) self.debug.write("translate_lines(%r) --> %r" % (lines, ret)) return ret def translate_arcs(self, arcs): ret = self.reporter.translate_arcs(arcs) self.debug.write("translate_arcs(%r) --> %r" % (arcs, ret)) return ret def no_branch_lines(self): ret = self.reporter.no_branch_lines() self.debug.write("no_branch_lines() --> %r" % (ret,)) return ret def exit_counts(self): ret = self.reporter.exit_counts() self.debug.write("exit_counts() --> %r" % (ret,)) return ret def arcs(self): ret = self.reporter.arcs() self.debug.write("arcs() --> %r" % (ret,)) return ret def source(self): ret = self.reporter.source() self.debug.write("source() --> %d chars" % (len(ret),)) return ret def source_token_lines(self): ret = list(self.reporter.source_token_lines()) self.debug.write("source_token_lines() --> %d tokens" % (len(ret),)) return ret python-coverage-4.5+dfsg.1.orig/coverage/version.py0000644000076600000620000000235413235412414020313 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """The version and URL for coverage.py""" # This file is exec'ed in setup.py, don't import anything! # Same semantics as sys.version_info. version_info = (4, 5, 0, 'final', 0) def _make_version(major, minor, micro, releaselevel, serial): """Create a readable version string from version_info tuple components.""" assert releaselevel in ['alpha', 'beta', 'candidate', 'final'] version = "%d.%d" % (major, minor) if micro: version += ".%d" % (micro,) if releaselevel != 'final': short = {'alpha': 'a', 'beta': 'b', 'candidate': 'rc'}[releaselevel] version += "%s%d" % (short, serial) return version def _make_url(major, minor, micro, releaselevel, serial): """Make the URL people should start at for this version of coverage.py.""" url = "https://coverage.readthedocs.io" if releaselevel != 'final': # For pre-releases, use a version-specific URL. url += "/en/coverage-" + _make_version(major, minor, micro, releaselevel, serial) return url __version__ = _make_version(*version_info) __url__ = _make_url(*version_info) python-coverage-4.5+dfsg.1.orig/coverage/config.py0000644000076600000620000004134213231145667020104 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Config file for coverage.py""" import collections import os import re import sys from coverage.backward import configparser, iitems, string_class from coverage.misc import contract, CoverageException, isolate_module os = isolate_module(os) class HandyConfigParser(configparser.RawConfigParser): """Our specialization of ConfigParser.""" def __init__(self, our_file): """Create the HandyConfigParser. `our_file` is True if this config file is specifically for coverage, False if we are examining another config file (tox.ini, setup.cfg) for possible settings. """ configparser.RawConfigParser.__init__(self) self.section_prefixes = ["coverage:"] if our_file: self.section_prefixes.append("") def read(self, filenames): """Read a file name as UTF-8 configuration data.""" kwargs = {} if sys.version_info >= (3, 2): kwargs['encoding'] = "utf-8" return configparser.RawConfigParser.read(self, filenames, **kwargs) def has_option(self, section, option): for section_prefix in self.section_prefixes: real_section = section_prefix + section has = configparser.RawConfigParser.has_option(self, real_section, option) if has: return has return False def has_section(self, section): for section_prefix in self.section_prefixes: real_section = section_prefix + section has = configparser.RawConfigParser.has_section(self, real_section) if has: return real_section return False def options(self, section): for section_prefix in self.section_prefixes: real_section = section_prefix + section if configparser.RawConfigParser.has_section(self, real_section): return configparser.RawConfigParser.options(self, real_section) raise configparser.NoSectionError def get_section(self, section): """Get the contents of a section, as a dictionary.""" d = {} for opt in self.options(section): d[opt] = self.get(section, opt) return d def get(self, section, option, *args, **kwargs): # pylint: disable=arguments-differ """Get a value, replacing environment variables also. The arguments are the same as `RawConfigParser.get`, but in the found value, ``$WORD`` or ``${WORD}`` are replaced by the value of the environment variable ``WORD``. Returns the finished value. """ for section_prefix in self.section_prefixes: real_section = section_prefix + section if configparser.RawConfigParser.has_option(self, real_section, option): break else: raise configparser.NoOptionError v = configparser.RawConfigParser.get(self, real_section, option, *args, **kwargs) def dollar_replace(m): """Called for each $replacement.""" # Only one of the groups will have matched, just get its text. word = next(w for w in m.groups() if w is not None) # pragma: part covered if word == "$": return "$" else: return os.environ.get(word, '') dollar_pattern = r"""(?x) # Use extended regex syntax \$(?: # A dollar sign, then (?P\w+) | # a plain word, {(?P\w+)} | # or a {-wrapped word, (?P[$]) # or a dollar sign. ) """ v = re.sub(dollar_pattern, dollar_replace, v) return v def getlist(self, section, option): """Read a list of strings. The value of `section` and `option` is treated as a comma- and newline- separated list of strings. Each value is stripped of whitespace. Returns the list of strings. """ value_list = self.get(section, option) values = [] for value_line in value_list.split('\n'): for value in value_line.split(','): value = value.strip() if value: values.append(value) return values def getregexlist(self, section, option): """Read a list of full-line regexes. The value of `section` and `option` is treated as a newline-separated list of regexes. Each value is stripped of whitespace. Returns the list of strings. """ line_list = self.get(section, option) value_list = [] for value in line_list.splitlines(): value = value.strip() try: re.compile(value) except re.error as e: raise CoverageException( "Invalid [%s].%s value %r: %s" % (section, option, value, e) ) if value: value_list.append(value) return value_list # The default line exclusion regexes. DEFAULT_EXCLUDE = [ r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)', ] # The default partial branch regexes, to be modified by the user. DEFAULT_PARTIAL = [ r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(branch|BRANCH)', ] # The default partial branch regexes, based on Python semantics. # These are any Python branching constructs that can't actually execute all # their branches. DEFAULT_PARTIAL_ALWAYS = [ 'while (True|1|False|0):', 'if (True|1|False|0):', ] class CoverageConfig(object): """Coverage.py configuration. The attributes of this class are the various settings that control the operation of coverage.py. """ def __init__(self): """Initialize the configuration attributes to their defaults.""" # Metadata about the config. self.attempted_config_files = [] self.config_files = [] # Defaults for [run] and [report] self._include = None self._omit = None # Defaults for [run] self.branch = False self.concurrency = None self.cover_pylib = False self.data_file = ".coverage" self.debug = [] self.disable_warnings = [] self.note = None self.parallel = False self.plugins = [] self.source = None self.run_include = None self.run_omit = None self.timid = False # Defaults for [report] self.exclude_list = DEFAULT_EXCLUDE[:] self.fail_under = 0.0 self.ignore_errors = False self.report_include = None self.report_omit = None self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:] self.partial_list = DEFAULT_PARTIAL[:] self.precision = 0 self.show_missing = False self.skip_covered = False # Defaults for [html] self.extra_css = None self.html_dir = "htmlcov" self.html_title = "Coverage report" # Defaults for [xml] self.xml_output = "coverage.xml" self.xml_package_depth = 99 # Defaults for [paths] self.paths = {} # Options for plugins self.plugin_options = {} MUST_BE_LIST = [ "debug", "concurrency", "plugins", "report_omit", "report_include", "run_omit", "run_include", ] def from_args(self, **kwargs): """Read config values from `kwargs`.""" for k, v in iitems(kwargs): if v is not None: if k in self.MUST_BE_LIST and isinstance(v, string_class): v = [v] setattr(self, k, v) @contract(filename=str) def from_file(self, filename, our_file): """Read configuration from a .rc file. `filename` is a file name to read. `our_file` is True if this config file is specifically for coverage, False if we are examining another config file (tox.ini, setup.cfg) for possible settings. Returns True or False, whether the file could be read, and it had some coverage.py settings in it. """ self.attempted_config_files.append(filename) cp = HandyConfigParser(our_file) try: files_read = cp.read(filename) except configparser.Error as err: raise CoverageException("Couldn't read config file %s: %s" % (filename, err)) if not files_read: return False self.config_files.extend(files_read) any_set = False try: for option_spec in self.CONFIG_FILE_OPTIONS: was_set = self._set_attr_from_config_option(cp, *option_spec) if was_set: any_set = True except ValueError as err: raise CoverageException("Couldn't read config file %s: %s" % (filename, err)) # Check that there are no unrecognized options. all_options = collections.defaultdict(set) for option_spec in self.CONFIG_FILE_OPTIONS: section, option = option_spec[1].split(":") all_options[section].add(option) for section, options in iitems(all_options): real_section = cp.has_section(section) if real_section: for unknown in set(cp.options(section)) - options: raise CoverageException( "Unrecognized option '[%s] %s=' in config file %s" % ( real_section, unknown, filename ) ) # [paths] is special if cp.has_section('paths'): for option in cp.options('paths'): self.paths[option] = cp.getlist('paths', option) any_set = True # plugins can have options for plugin in self.plugins: if cp.has_section(plugin): self.plugin_options[plugin] = cp.get_section(plugin) any_set = True # Was this file used as a config file? If it's specifically our file, # then it was used. If we're piggybacking on someone else's file, # then it was only used if we found some settings in it. if our_file: return True else: return any_set CONFIG_FILE_OPTIONS = [ # These are *args for _set_attr_from_config_option: # (attr, where, type_="") # # attr is the attribute to set on the CoverageConfig object. # where is the section:name to read from the configuration file. # type_ is the optional type to apply, by using .getTYPE to read the # configuration value from the file. # [run] ('branch', 'run:branch', 'boolean'), ('concurrency', 'run:concurrency', 'list'), ('cover_pylib', 'run:cover_pylib', 'boolean'), ('data_file', 'run:data_file'), ('debug', 'run:debug', 'list'), ('disable_warnings', 'run:disable_warnings', 'list'), ('note', 'run:note'), ('parallel', 'run:parallel', 'boolean'), ('plugins', 'run:plugins', 'list'), ('run_include', 'run:include', 'list'), ('run_omit', 'run:omit', 'list'), ('source', 'run:source', 'list'), ('timid', 'run:timid', 'boolean'), # [report] ('exclude_list', 'report:exclude_lines', 'regexlist'), ('fail_under', 'report:fail_under', 'float'), ('ignore_errors', 'report:ignore_errors', 'boolean'), ('partial_always_list', 'report:partial_branches_always', 'regexlist'), ('partial_list', 'report:partial_branches', 'regexlist'), ('precision', 'report:precision', 'int'), ('report_include', 'report:include', 'list'), ('report_omit', 'report:omit', 'list'), ('show_missing', 'report:show_missing', 'boolean'), ('skip_covered', 'report:skip_covered', 'boolean'), ('sort', 'report:sort'), # [html] ('extra_css', 'html:extra_css'), ('html_dir', 'html:directory'), ('html_title', 'html:title'), # [xml] ('xml_output', 'xml:output'), ('xml_package_depth', 'xml:package_depth', 'int'), ] def _set_attr_from_config_option(self, cp, attr, where, type_=''): """Set an attribute on self if it exists in the ConfigParser. Returns True if the attribute was set. """ section, option = where.split(":") if cp.has_option(section, option): method = getattr(cp, 'get' + type_) setattr(self, attr, method(section, option)) return True return False def get_plugin_options(self, plugin): """Get a dictionary of options for the plugin named `plugin`.""" return self.plugin_options.get(plugin, {}) def set_option(self, option_name, value): """Set an option in the configuration. `option_name` is a colon-separated string indicating the section and option name. For example, the ``branch`` option in the ``[run]`` section of the config file would be indicated with `"run:branch"`. `value` is the new value for the option. """ # Check all the hard-coded options. for option_spec in self.CONFIG_FILE_OPTIONS: attr, where = option_spec[:2] if where == option_name: setattr(self, attr, value) return # See if it's a plugin option. plugin_name, _, key = option_name.partition(":") if key and plugin_name in self.plugins: self.plugin_options.setdefault(plugin_name, {})[key] = value return # If we get here, we didn't find the option. raise CoverageException("No such option: %r" % option_name) def get_option(self, option_name): """Get an option from the configuration. `option_name` is a colon-separated string indicating the section and option name. For example, the ``branch`` option in the ``[run]`` section of the config file would be indicated with `"run:branch"`. Returns the value of the option. """ # Check all the hard-coded options. for option_spec in self.CONFIG_FILE_OPTIONS: attr, where = option_spec[:2] if where == option_name: return getattr(self, attr) # See if it's a plugin option. plugin_name, _, key = option_name.partition(":") if key and plugin_name in self.plugins: return self.plugin_options.get(plugin_name, {}).get(key) # If we get here, we didn't find the option. raise CoverageException("No such option: %r" % option_name) def read_coverage_config(config_file, **kwargs): """Read the coverage.py configuration. Arguments: config_file: a boolean or string, see the `Coverage` class for the tricky details. all others: keyword arguments from the `Coverage` class, used for setting values in the configuration. Returns: config_file, config: config_file is the value to use for config_file in other invocations of coverage. config is a CoverageConfig object read from the appropriate configuration file. """ # Build the configuration from a number of sources: # 1) defaults: config = CoverageConfig() # 2) from a file: if config_file: # Some API users were specifying ".coveragerc" to mean the same as # True, so make it so. if config_file == ".coveragerc": config_file = True specified_file = (config_file is not True) if not specified_file: config_file = ".coveragerc" for fname, our_file in [(config_file, True), ("setup.cfg", False), ("tox.ini", False)]: config_read = config.from_file(fname, our_file=our_file) is_config_file = fname == config_file if not config_read and is_config_file and specified_file: raise CoverageException("Couldn't read '%s' as a config file" % fname) if config_read: break # 3) from environment variables: env_data_file = os.environ.get('COVERAGE_FILE') if env_data_file: config.data_file = env_data_file debugs = os.environ.get('COVERAGE_DEBUG') if debugs: config.debug.extend(d.strip() for d in debugs.split(",")) # 4) from constructor arguments: config.from_args(**kwargs) # Once all the config has been collected, there's a little post-processing # to do. config.data_file = os.path.expanduser(config.data_file) config.html_dir = os.path.expanduser(config.html_dir) config.xml_output = os.path.expanduser(config.xml_output) return config_file, config python-coverage-4.5+dfsg.1.orig/coverage/annotate.py0000644000076600000620000000645013173342030020435 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Source file annotation for coverage.py.""" import io import os import re from coverage.files import flat_rootname from coverage.misc import isolate_module from coverage.report import Reporter os = isolate_module(os) class AnnotateReporter(Reporter): """Generate annotated source files showing line coverage. This reporter creates annotated copies of the measured source files. Each .py file is copied as a .py,cover file, with a left-hand margin annotating each line:: > def h(x): - if 0: #pragma: no cover - pass > if x == 1: ! a = 1 > else: > a = 2 > h(2) Executed lines use '>', lines not executed use '!', lines excluded from consideration use '-'. """ def __init__(self, coverage, config): super(AnnotateReporter, self).__init__(coverage, config) self.directory = None blank_re = re.compile(r"\s*(#|$)") else_re = re.compile(r"\s*else\s*:\s*(#|$)") def report(self, morfs, directory=None): """Run the report. See `coverage.report()` for arguments. """ self.report_files(self.annotate_file, morfs, directory) def annotate_file(self, fr, analysis): """Annotate a single file. `fr` is the FileReporter for the file to annotate. """ statements = sorted(analysis.statements) missing = sorted(analysis.missing) excluded = sorted(analysis.excluded) if self.directory: dest_file = os.path.join(self.directory, flat_rootname(fr.relative_filename())) if dest_file.endswith("_py"): dest_file = dest_file[:-3] + ".py" dest_file += ",cover" else: dest_file = fr.filename + ",cover" with io.open(dest_file, 'w', encoding='utf8') as dest: i = 0 j = 0 covered = True source = fr.source() for lineno, line in enumerate(source.splitlines(True), start=1): while i < len(statements) and statements[i] < lineno: i += 1 while j < len(missing) and missing[j] < lineno: j += 1 if i < len(statements) and statements[i] == lineno: covered = j >= len(missing) or missing[j] > lineno if self.blank_re.match(line): dest.write(u' ') elif self.else_re.match(line): # Special logic for lines containing only 'else:'. if i >= len(statements) and j >= len(missing): dest.write(u'! ') elif i >= len(statements) or j >= len(missing): dest.write(u'> ') elif statements[i] == missing[j]: dest.write(u'! ') else: dest.write(u'> ') elif lineno in excluded: dest.write(u'- ') elif covered: dest.write(u'> ') else: dest.write(u'! ') dest.write(line) python-coverage-4.5+dfsg.1.orig/coverage/multiproc.py0000644000076600000620000000703013146037571020650 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Monkey-patching to add multiprocessing support for coverage.py""" import multiprocessing import multiprocessing.process import os import sys from coverage.misc import contract # An attribute that will be set on the module to indicate that it has been # monkey-patched. PATCHED_MARKER = "_coverage$patched" # The environment variable that specifies the rcfile for subprocesses. COVERAGE_RCFILE_ENV = "_COVERAGE_RCFILE" if sys.version_info >= (3, 4): OriginalProcess = multiprocessing.process.BaseProcess else: OriginalProcess = multiprocessing.Process original_bootstrap = OriginalProcess._bootstrap class ProcessWithCoverage(OriginalProcess): """A replacement for multiprocess.Process that starts coverage.""" def _bootstrap(self): """Wrapper around _bootstrap to start coverage.""" from coverage import Coverage # avoid circular import rcfile = os.environ[COVERAGE_RCFILE_ENV] cov = Coverage(data_suffix=True, config_file=rcfile) cov.start() debug = cov.debug try: if debug.should("multiproc"): debug.write("Calling multiprocessing bootstrap") return original_bootstrap(self) finally: if debug.should("multiproc"): debug.write("Finished multiprocessing bootstrap") cov.stop() cov.save() if debug.should("multiproc"): debug.write("Saved multiprocessing data") class Stowaway(object): """An object to pickle, so when it is unpickled, it can apply the monkey-patch.""" def __init__(self, rcfile): self.rcfile = rcfile def __getstate__(self): return {'rcfile': self.rcfile} def __setstate__(self, state): patch_multiprocessing(state['rcfile']) @contract(rcfile=str) def patch_multiprocessing(rcfile): """Monkey-patch the multiprocessing module. This enables coverage measurement of processes started by multiprocessing. This involves aggressive monkey-patching. `rcfile` is the path to the rcfile being used. """ if hasattr(multiprocessing, PATCHED_MARKER): return if sys.version_info >= (3, 4): OriginalProcess._bootstrap = ProcessWithCoverage._bootstrap else: multiprocessing.Process = ProcessWithCoverage # Set the value in ProcessWithCoverage that will be pickled into the child # process. os.environ[COVERAGE_RCFILE_ENV] = rcfile # When spawning processes rather than forking them, we have no state in the # new process. We sneak in there with a Stowaway: we stuff one of our own # objects into the data that gets pickled and sent to the sub-process. When # the Stowaway is unpickled, it's __setstate__ method is called, which # re-applies the monkey-patch. # Windows only spawns, so this is needed to keep Windows working. try: from multiprocessing import spawn original_get_preparation_data = spawn.get_preparation_data except (ImportError, AttributeError): pass else: def get_preparation_data_with_stowaway(name): """Get the original preparation data, and also insert our stowaway.""" d = original_get_preparation_data(name) d['stowaway'] = Stowaway(rcfile) return d spawn.get_preparation_data = get_preparation_data_with_stowaway setattr(multiprocessing, PATCHED_MARKER, True) python-coverage-4.5+dfsg.1.orig/coverage/summary.py0000644000076600000620000001460713231225124020323 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Summary reporting""" import sys from coverage import env from coverage.report import Reporter from coverage.results import Numbers from coverage.misc import NotPython, CoverageException, output_encoding, StopEverything class SummaryReporter(Reporter): """A reporter for writing the summary report.""" def __init__(self, coverage, config): super(SummaryReporter, self).__init__(coverage, config) self.branches = coverage.data.has_arcs() def report(self, morfs, outfile=None): """Writes a report summarizing coverage statistics per module. `outfile` is a file object to write the summary to. It must be opened for native strings (bytes on Python 2, Unicode on Python 3). """ if outfile is None: outfile = sys.stdout def writeout(line): """Write a line to the output, adding a newline.""" if env.PY2: line = line.encode(output_encoding()) outfile.write(line.rstrip()) outfile.write("\n") fr_analysis = [] skipped_count = 0 total = Numbers() fmt_err = u"%s %s: %s" for fr in self.find_file_reporters(morfs): try: analysis = self.coverage._analyze(fr) nums = analysis.numbers total += nums if self.config.skip_covered: # Don't report on 100% files. no_missing_lines = (nums.n_missing == 0) no_missing_branches = (nums.n_partial_branches == 0) if no_missing_lines and no_missing_branches: skipped_count += 1 continue fr_analysis.append((fr, analysis)) except StopEverything: # Don't report this on single files, it's a systemic problem. raise except Exception: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] # NotPython is only raised by PythonFileReporter, which has a # should_be_python() method. if issubclass(typ, NotPython) and not fr.should_be_python(): report_it = False if report_it: writeout(fmt_err % (fr.relative_filename(), typ.__name__, msg)) # Prepare the formatting strings, header, and column sorting. max_name = max([len(fr.relative_filename()) for (fr, analysis) in fr_analysis] + [5]) fmt_name = u"%%- %ds " % max_name fmt_skip_covered = u"\n%s file%s skipped due to complete coverage." header = (fmt_name % "Name") + u" Stmts Miss" fmt_coverage = fmt_name + u"%6d %6d" if self.branches: header += u" Branch BrPart" fmt_coverage += u" %6d %6d" width100 = Numbers.pc_str_width() header += u"%*s" % (width100+4, "Cover") fmt_coverage += u"%%%ds%%%%" % (width100+3,) if self.config.show_missing: header += u" Missing" fmt_coverage += u" %s" rule = u"-" * len(header) column_order = dict(name=0, stmts=1, miss=2, cover=-1) if self.branches: column_order.update(dict(branch=3, brpart=4)) # Write the header writeout(header) writeout(rule) # `lines` is a list of pairs, (line text, line values). The line text # is a string that will be printed, and line values is a tuple of # sortable values. lines = [] for (fr, analysis) in fr_analysis: try: nums = analysis.numbers args = (fr.relative_filename(), nums.n_statements, nums.n_missing) if self.branches: args += (nums.n_branches, nums.n_partial_branches) args += (nums.pc_covered_str,) if self.config.show_missing: missing_fmtd = analysis.missing_formatted() if self.branches: branches_fmtd = analysis.arcs_missing_formatted() if branches_fmtd: if missing_fmtd: missing_fmtd += ", " missing_fmtd += branches_fmtd args += (missing_fmtd,) text = fmt_coverage % args # Add numeric percent coverage so that sorting makes sense. args += (nums.pc_covered,) lines.append((text, args)) except Exception: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] # NotPython is only raised by PythonFileReporter, which has a # should_be_python() method. if typ is NotPython and not fr.should_be_python(): report_it = False if report_it: writeout(fmt_err % (fr.relative_filename(), typ.__name__, msg)) # Sort the lines and write them out. if getattr(self.config, 'sort', None): position = column_order.get(self.config.sort.lower()) if position is None: raise CoverageException("Invalid sorting option: {0!r}".format(self.config.sort)) lines.sort(key=lambda l: (l[1][position], l[0])) for line in lines: writeout(line[0]) # Write a TOTAl line if we had more than one file. if total.n_files > 1: writeout(rule) args = ("TOTAL", total.n_statements, total.n_missing) if self.branches: args += (total.n_branches, total.n_partial_branches) args += (total.pc_covered_str,) if self.config.show_missing: args += ("",) writeout(fmt_coverage % args) # Write other final lines. if not total.n_files and not skipped_count: raise CoverageException("No data to report.") if self.config.skip_covered and skipped_count: writeout(fmt_skip_covered % (skipped_count, 's' if skipped_count > 1 else '')) return total.n_statements and total.pc_covered python-coverage-4.5+dfsg.1.orig/coverage/data.py0000644000076600000620000007023013231214671017536 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Coverage data for coverage.py.""" import glob import itertools import json import optparse import os import os.path import random import re import socket from coverage import env from coverage.backward import iitems, string_class from coverage.debug import _TEST_NAME_FILE from coverage.files import PathAliases from coverage.misc import CoverageException, file_be_gone, isolate_module os = isolate_module(os) class CoverageData(object): """Manages collected coverage data, including file storage. This class is the public supported API to the data coverage.py collects during program execution. It includes information about what code was executed. It does not include information from the analysis phase, to determine what lines could have been executed, or what lines were not executed. .. note:: The file format is not documented or guaranteed. It will change in the future, in possibly complicated ways. Do not read coverage.py data files directly. Use this API to avoid disruption. There are a number of kinds of data that can be collected: * **lines**: the line numbers of source lines that were executed. These are always available. * **arcs**: pairs of source and destination line numbers for transitions between source lines. These are only available if branch coverage was used. * **file tracer names**: the module names of the file tracer plugins that handled each file in the data. * **run information**: information about the program execution. This is written during "coverage run", and then accumulated during "coverage combine". Lines, arcs, and file tracer names are stored for each source file. File names in this API are case-sensitive, even on platforms with case-insensitive file systems. To read a coverage.py data file, use :meth:`read_file`, or :meth:`read_fileobj` if you have an already-opened file. You can then access the line, arc, or file tracer data with :meth:`lines`, :meth:`arcs`, or :meth:`file_tracer`. Run information is available with :meth:`run_infos`. The :meth:`has_arcs` method indicates whether arc data is available. You can get a list of the files in the data with :meth:`measured_files`. A summary of the line data is available from :meth:`line_counts`. As with most Python containers, you can determine if there is any data at all by using this object as a boolean value. Most data files will be created by coverage.py itself, but you can use methods here to create data files if you like. The :meth:`add_lines`, :meth:`add_arcs`, and :meth:`add_file_tracers` methods add data, in ways that are convenient for coverage.py. The :meth:`add_run_info` method adds key-value pairs to the run information. To add a file without any measured data, use :meth:`touch_file`. You write to a named file with :meth:`write_file`, or to an already opened file with :meth:`write_fileobj`. You can clear the data in memory with :meth:`erase`. Two data collections can be combined by using :meth:`update` on one :class:`CoverageData`, passing it the other. """ # The data file format is JSON, with these keys: # # * lines: a dict mapping file names to lists of line numbers # executed:: # # { "file1": [17,23,45], "file2": [1,2,3], ... } # # * arcs: a dict mapping file names to lists of line number pairs:: # # { "file1": [[17,23], [17,25], [25,26]], ... } # # * file_tracers: a dict mapping file names to plugin names:: # # { "file1": "django.coverage", ... } # # * runs: a list of dicts of information about the coverage.py runs # contributing to the data:: # # [ { "brief_sys": "CPython 2.7.10 Darwin" }, ... ] # # Only one of `lines` or `arcs` will be present: with branch coverage, data # is stored as arcs. Without branch coverage, it is stored as lines. The # line data is easily recovered from the arcs: it is all the first elements # of the pairs that are greater than zero. def __init__(self, debug=None): """Create a CoverageData. `debug` is a `DebugControl` object for writing debug messages. """ self._debug = debug # A map from canonical Python source file name to a dictionary in # which there's an entry for each line number that has been # executed: # # { 'filename1.py': [12, 47, 1001], ... } # self._lines = None # A map from canonical Python source file name to a dictionary with an # entry for each pair of line numbers forming an arc: # # { 'filename1.py': [(12,14), (47,48), ... ], ... } # self._arcs = None # A map from canonical source file name to a plugin module name: # # { 'filename1.py': 'django.coverage', ... } # self._file_tracers = {} # A list of dicts of information about the coverage.py runs. self._runs = [] def __repr__(self): return "<{klass} lines={lines} arcs={arcs} tracers={tracers} runs={runs}>".format( klass=self.__class__.__name__, lines="None" if self._lines is None else "{{{0}}}".format(len(self._lines)), arcs="None" if self._arcs is None else "{{{0}}}".format(len(self._arcs)), tracers="{{{0}}}".format(len(self._file_tracers)), runs="[{0}]".format(len(self._runs)), ) ## ## Reading data ## def has_arcs(self): """Does this data have arcs? Arc data is only available if branch coverage was used during collection. Returns a boolean. """ return self._has_arcs() def lines(self, filename): """Get the list of lines executed for a file. If the file was not measured, returns None. A file might be measured, and have no lines executed, in which case an empty list is returned. If the file was executed, returns a list of integers, the line numbers executed in the file. The list is in no particular order. """ if self._arcs is not None: arcs = self._arcs.get(filename) if arcs is not None: all_lines = itertools.chain.from_iterable(arcs) return list(set(l for l in all_lines if l > 0)) elif self._lines is not None: return self._lines.get(filename) return None def arcs(self, filename): """Get the list of arcs executed for a file. If the file was not measured, returns None. A file might be measured, and have no arcs executed, in which case an empty list is returned. If the file was executed, returns a list of 2-tuples of integers. Each pair is a starting line number and an ending line number for a transition from one line to another. The list is in no particular order. Negative numbers have special meaning. If the starting line number is -N, it represents an entry to the code object that starts at line N. If the ending ling number is -N, it's an exit from the code object that starts at line N. """ if self._arcs is not None: if filename in self._arcs: return self._arcs[filename] return None def file_tracer(self, filename): """Get the plugin name of the file tracer for a file. Returns the name of the plugin that handles this file. If the file was measured, but didn't use a plugin, then "" is returned. If the file was not measured, then None is returned. """ # Because the vast majority of files involve no plugin, we don't store # them explicitly in self._file_tracers. Check the measured data # instead to see if it was a known file with no plugin. if filename in (self._arcs or self._lines or {}): return self._file_tracers.get(filename, "") return None def run_infos(self): """Return the list of dicts of run information. For data collected during a single run, this will be a one-element list. If data has been combined, there will be one element for each original data file. """ return self._runs def measured_files(self): """A list of all files that had been measured.""" return list(self._arcs or self._lines or {}) def line_counts(self, fullpath=False): """Return a dict summarizing the line coverage data. Keys are based on the file names, and values are the number of executed lines. If `fullpath` is true, then the keys are the full pathnames of the files, otherwise they are the basenames of the files. Returns a dict mapping file names to counts of lines. """ summ = {} if fullpath: filename_fn = lambda f: f else: filename_fn = os.path.basename for filename in self.measured_files(): summ[filename_fn(filename)] = len(self.lines(filename)) return summ def __nonzero__(self): return bool(self._lines or self._arcs) __bool__ = __nonzero__ def read_fileobj(self, file_obj): """Read the coverage data from the given file object. Should only be used on an empty CoverageData object. """ data = self._read_raw_data(file_obj) self._lines = self._arcs = None if 'lines' in data: self._lines = data['lines'] if 'arcs' in data: self._arcs = dict( (fname, [tuple(pair) for pair in arcs]) for fname, arcs in iitems(data['arcs']) ) self._file_tracers = data.get('file_tracers', {}) self._runs = data.get('runs', []) self._validate() def read_file(self, filename): """Read the coverage data from `filename` into this object.""" if self._debug and self._debug.should('dataio'): self._debug.write("Reading data from %r" % (filename,)) try: with self._open_for_reading(filename) as f: self.read_fileobj(f) except Exception as exc: raise CoverageException( "Couldn't read data from '%s': %s: %s" % ( filename, exc.__class__.__name__, exc, ) ) _GO_AWAY = "!coverage.py: This is a private format, don't read it directly!" @classmethod def _open_for_reading(cls, filename): """Open a file appropriately for reading data.""" return open(filename, "r") @classmethod def _read_raw_data(cls, file_obj): """Read the raw data from a file object.""" go_away = file_obj.read(len(cls._GO_AWAY)) if go_away != cls._GO_AWAY: raise CoverageException("Doesn't seem to be a coverage.py data file") return json.load(file_obj) @classmethod def _read_raw_data_file(cls, filename): """Read the raw data from a file, for debugging.""" with cls._open_for_reading(filename) as f: return cls._read_raw_data(f) ## ## Writing data ## def add_lines(self, line_data): """Add measured line data. `line_data` is a dictionary mapping file names to dictionaries:: { filename: { lineno: None, ... }, ...} """ if self._debug and self._debug.should('dataop'): self._debug.write("Adding lines: %d files, %d lines total" % ( len(line_data), sum(len(lines) for lines in line_data.values()) )) if self._has_arcs(): raise CoverageException("Can't add lines to existing arc data") if self._lines is None: self._lines = {} for filename, linenos in iitems(line_data): if filename in self._lines: new_linenos = set(self._lines[filename]) new_linenos.update(linenos) linenos = new_linenos self._lines[filename] = list(linenos) self._validate() def add_arcs(self, arc_data): """Add measured arc data. `arc_data` is a dictionary mapping file names to dictionaries:: { filename: { (l1,l2): None, ... }, ...} """ if self._debug and self._debug.should('dataop'): self._debug.write("Adding arcs: %d files, %d arcs total" % ( len(arc_data), sum(len(arcs) for arcs in arc_data.values()) )) if self._has_lines(): raise CoverageException("Can't add arcs to existing line data") if self._arcs is None: self._arcs = {} for filename, arcs in iitems(arc_data): if filename in self._arcs: new_arcs = set(self._arcs[filename]) new_arcs.update(arcs) arcs = new_arcs self._arcs[filename] = list(arcs) self._validate() def add_file_tracers(self, file_tracers): """Add per-file plugin information. `file_tracers` is { filename: plugin_name, ... } """ if self._debug and self._debug.should('dataop'): self._debug.write("Adding file tracers: %d files" % (len(file_tracers),)) existing_files = self._arcs or self._lines or {} for filename, plugin_name in iitems(file_tracers): if filename not in existing_files: raise CoverageException( "Can't add file tracer data for unmeasured file '%s'" % (filename,) ) existing_plugin = self._file_tracers.get(filename) if existing_plugin is not None and plugin_name != existing_plugin: raise CoverageException( "Conflicting file tracer name for '%s': %r vs %r" % ( filename, existing_plugin, plugin_name, ) ) self._file_tracers[filename] = plugin_name self._validate() def add_run_info(self, **kwargs): """Add information about the run. Keywords are arbitrary, and are stored in the run dictionary. Values must be JSON serializable. You may use this function more than once, but repeated keywords overwrite each other. """ if self._debug and self._debug.should('dataop'): self._debug.write("Adding run info: %r" % (kwargs,)) if not self._runs: self._runs = [{}] self._runs[0].update(kwargs) self._validate() def touch_file(self, filename, plugin_name=""): """Ensure that `filename` appears in the data, empty if needed. `plugin_name` is the name of the plugin resposible for this file. It is used to associate the right filereporter, etc. """ if self._debug and self._debug.should('dataop'): self._debug.write("Touching %r" % (filename,)) if not self._has_arcs() and not self._has_lines(): raise CoverageException("Can't touch files in an empty CoverageData") if self._has_arcs(): where = self._arcs else: where = self._lines where.setdefault(filename, []) if plugin_name: # Set the tracer for this file self._file_tracers[filename] = plugin_name self._validate() def write_fileobj(self, file_obj): """Write the coverage data to `file_obj`.""" # Create the file data. file_data = {} if self._has_arcs(): file_data['arcs'] = self._arcs if self._has_lines(): file_data['lines'] = self._lines if self._file_tracers: file_data['file_tracers'] = self._file_tracers if self._runs: file_data['runs'] = self._runs # Write the data to the file. file_obj.write(self._GO_AWAY) json.dump(file_data, file_obj, separators=(',', ':')) def write_file(self, filename): """Write the coverage data to `filename`.""" if self._debug and self._debug.should('dataio'): self._debug.write("Writing data to %r" % (filename,)) with open(filename, 'w') as fdata: self.write_fileobj(fdata) def erase(self): """Erase the data in this object.""" self._lines = None self._arcs = None self._file_tracers = {} self._runs = [] self._validate() def update(self, other_data, aliases=None): """Update this data with data from another `CoverageData`. If `aliases` is provided, it's a `PathAliases` object that is used to re-map paths to match the local machine's. """ if self._has_lines() and other_data._has_arcs(): raise CoverageException("Can't combine arc data with line data") if self._has_arcs() and other_data._has_lines(): raise CoverageException("Can't combine line data with arc data") aliases = aliases or PathAliases() # _file_tracers: only have a string, so they have to agree. # Have to do these first, so that our examination of self._arcs and # self._lines won't be confused by data updated from other_data. for filename in other_data.measured_files(): other_plugin = other_data.file_tracer(filename) filename = aliases.map(filename) this_plugin = self.file_tracer(filename) if this_plugin is None: if other_plugin: self._file_tracers[filename] = other_plugin elif this_plugin != other_plugin: raise CoverageException( "Conflicting file tracer name for '%s': %r vs %r" % ( filename, this_plugin, other_plugin, ) ) # _runs: add the new runs to these runs. self._runs.extend(other_data._runs) # _lines: merge dicts. if other_data._has_lines(): if self._lines is None: self._lines = {} for filename, file_lines in iitems(other_data._lines): filename = aliases.map(filename) if filename in self._lines: lines = set(self._lines[filename]) lines.update(file_lines) file_lines = list(lines) self._lines[filename] = file_lines # _arcs: merge dicts. if other_data._has_arcs(): if self._arcs is None: self._arcs = {} for filename, file_arcs in iitems(other_data._arcs): filename = aliases.map(filename) if filename in self._arcs: arcs = set(self._arcs[filename]) arcs.update(file_arcs) file_arcs = list(arcs) self._arcs[filename] = file_arcs self._validate() ## ## Miscellaneous ## def _validate(self): """If we are in paranoid mode, validate that everything is right.""" if env.TESTING: self._validate_invariants() def _validate_invariants(self): """Validate internal invariants.""" # Only one of _lines or _arcs should exist. assert not(self._has_lines() and self._has_arcs()), ( "Shouldn't have both _lines and _arcs" ) # _lines should be a dict of lists of ints. if self._has_lines(): for fname, lines in iitems(self._lines): assert isinstance(fname, string_class), "Key in _lines shouldn't be %r" % (fname,) assert all(isinstance(x, int) for x in lines), ( "_lines[%r] shouldn't be %r" % (fname, lines) ) # _arcs should be a dict of lists of pairs of ints. if self._has_arcs(): for fname, arcs in iitems(self._arcs): assert isinstance(fname, string_class), "Key in _arcs shouldn't be %r" % (fname,) assert all(isinstance(x, int) and isinstance(y, int) for x, y in arcs), ( "_arcs[%r] shouldn't be %r" % (fname, arcs) ) # _file_tracers should have only non-empty strings as values. for fname, plugin in iitems(self._file_tracers): assert isinstance(fname, string_class), ( "Key in _file_tracers shouldn't be %r" % (fname,) ) assert plugin and isinstance(plugin, string_class), ( "_file_tracers[%r] shoudn't be %r" % (fname, plugin) ) # _runs should be a list of dicts. for val in self._runs: assert isinstance(val, dict) for key in val: assert isinstance(key, string_class), "Key in _runs shouldn't be %r" % (key,) def add_to_hash(self, filename, hasher): """Contribute `filename`'s data to the `hasher`. `hasher` is a `coverage.misc.Hasher` instance to be updated with the file's data. It should only get the results data, not the run data. """ if self._has_arcs(): hasher.update(sorted(self.arcs(filename) or [])) else: hasher.update(sorted(self.lines(filename) or [])) hasher.update(self.file_tracer(filename)) ## ## Internal ## def _has_lines(self): """Do we have data in self._lines?""" return self._lines is not None def _has_arcs(self): """Do we have data in self._arcs?""" return self._arcs is not None class CoverageDataFiles(object): """Manage the use of coverage data files.""" def __init__(self, basename=None, warn=None, debug=None): """Create a CoverageDataFiles to manage data files. `warn` is the warning function to use. `basename` is the name of the file to use for storing data. `debug` is a `DebugControl` object for writing debug messages. """ self.warn = warn self.debug = debug # Construct the file name that will be used for data storage. self.filename = os.path.abspath(basename or ".coverage") def erase(self, parallel=False): """Erase the data from the file storage. If `parallel` is true, then also deletes data files created from the basename by parallel-mode. """ if self.debug and self.debug.should('dataio'): self.debug.write("Erasing data file %r" % (self.filename,)) file_be_gone(self.filename) if parallel: data_dir, local = os.path.split(self.filename) localdot = local + '.*' pattern = os.path.join(os.path.abspath(data_dir), localdot) for filename in glob.glob(pattern): if self.debug and self.debug.should('dataio'): self.debug.write("Erasing parallel data file %r" % (filename,)) file_be_gone(filename) def read(self, data): """Read the coverage data.""" if os.path.exists(self.filename): data.read_file(self.filename) def write(self, data, suffix=None): """Write the collected coverage data to a file. `suffix` is a suffix to append to the base file name. This can be used for multiple or parallel execution, so that many coverage data files can exist simultaneously. A dot will be used to join the base name and the suffix. """ filename = self.filename if suffix is True: # If data_suffix was a simple true value, then make a suffix with # plenty of distinguishing information. We do this here in # `save()` at the last minute so that the pid will be correct even # if the process forks. extra = "" if _TEST_NAME_FILE: # pragma: debugging with open(_TEST_NAME_FILE) as f: test_name = f.read() extra = "." + test_name dice = random.Random(os.urandom(8)).randint(0, 999999) suffix = "%s%s.%s.%06d" % (socket.gethostname(), extra, os.getpid(), dice) if suffix: filename += "." + suffix data.write_file(filename) def combine_parallel_data(self, data, aliases=None, data_paths=None, strict=False): """Combine a number of data files together. Treat `self.filename` as a file prefix, and combine the data from all of the data files starting with that prefix plus a dot. If `aliases` is provided, it's a `PathAliases` object that is used to re-map paths to match the local machine's. If `data_paths` is provided, it is a list of directories or files to combine. Directories are searched for files that start with `self.filename` plus dot as a prefix, and those files are combined. If `data_paths` is not provided, then the directory portion of `self.filename` is used as the directory to search for data files. Every data file found and combined is then deleted from disk. If a file cannot be read, a warning will be issued, and the file will not be deleted. If `strict` is true, and no files are found to combine, an error is raised. """ # Because of the os.path.abspath in the constructor, data_dir will # never be an empty string. data_dir, local = os.path.split(self.filename) localdot = local + '.*' data_paths = data_paths or [data_dir] files_to_combine = [] for p in data_paths: if os.path.isfile(p): files_to_combine.append(os.path.abspath(p)) elif os.path.isdir(p): pattern = os.path.join(os.path.abspath(p), localdot) files_to_combine.extend(glob.glob(pattern)) else: raise CoverageException("Couldn't combine from non-existent path '%s'" % (p,)) if strict and not files_to_combine: raise CoverageException("No data to combine") files_combined = 0 for f in files_to_combine: new_data = CoverageData(debug=self.debug) try: new_data.read_file(f) except CoverageException as exc: if self.warn: # The CoverageException has the file name in it, so just # use the message as the warning. self.warn(str(exc)) else: data.update(new_data, aliases=aliases) files_combined += 1 if self.debug and self.debug.should('dataio'): self.debug.write("Deleting combined data file %r" % (f,)) file_be_gone(f) if strict and not files_combined: raise CoverageException("No usable data files") def canonicalize_json_data(data): """Canonicalize our JSON data so it can be compared.""" for fname, lines in iitems(data.get('lines', {})): data['lines'][fname] = sorted(lines) for fname, arcs in iitems(data.get('arcs', {})): data['arcs'][fname] = sorted(arcs) def pretty_data(data): """Format data as JSON, but as nicely as possible. Returns a string. """ # Start with a basic JSON dump. out = json.dumps(data, indent=4, sort_keys=True) # But pairs of numbers shouldn't be split across lines... out = re.sub(r"\[\s+(-?\d+),\s+(-?\d+)\s+]", r"[\1, \2]", out) # Trailing spaces mess with tests, get rid of them. out = re.sub(r"(?m)\s+$", "", out) return out def debug_main(args): """Dump the raw data from data files. Run this as:: $ python -m coverage.data [FILE] """ parser = optparse.OptionParser() parser.add_option( "-c", "--canonical", action="store_true", help="Sort data into a canonical order", ) options, args = parser.parse_args(args) for filename in (args or [".coverage"]): print("--- {0} ------------------------------".format(filename)) data = CoverageData._read_raw_data_file(filename) if options.canonical: canonicalize_json_data(data) print(pretty_data(data)) if __name__ == '__main__': import sys debug_main(sys.argv[1:]) python-coverage-4.5+dfsg.1.orig/coverage/misc.py0000644000076600000620000002151713173515667017602 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Miscellaneous stuff for coverage.py.""" import errno import hashlib import inspect import locale import os import sys import types from coverage import env from coverage.backward import to_bytes, unicode_class ISOLATED_MODULES = {} def isolate_module(mod): """Copy a module so that we are isolated from aggressive mocking. If a test suite mocks os.path.exists (for example), and then we need to use it during the test, everything will get tangled up if we use their mock. Making a copy of the module when we import it will isolate coverage.py from those complications. """ if mod not in ISOLATED_MODULES: new_mod = types.ModuleType(mod.__name__) ISOLATED_MODULES[mod] = new_mod for name in dir(mod): value = getattr(mod, name) if isinstance(value, types.ModuleType): value = isolate_module(value) setattr(new_mod, name, value) return ISOLATED_MODULES[mod] os = isolate_module(os) def dummy_decorator_with_args(*args_unused, **kwargs_unused): """Dummy no-op implementation of a decorator with arguments.""" def _decorator(func): return func return _decorator # Use PyContracts for assertion testing on parameters and returns, but only if # we are running our own test suite. if env.TESTING: from contracts import contract # pylint: disable=unused-import from contracts import new_contract as raw_new_contract def new_contract(*args, **kwargs): """A proxy for contracts.new_contract that doesn't mind happening twice.""" try: return raw_new_contract(*args, **kwargs) except ValueError: # During meta-coverage, this module is imported twice, and # PyContracts doesn't like redefining contracts. It's OK. pass # Define contract words that PyContract doesn't have. new_contract('bytes', lambda v: isinstance(v, bytes)) if env.PY3: new_contract('unicode', lambda v: isinstance(v, unicode_class)) def one_of(argnames): """Ensure that only one of the argnames is non-None.""" def _decorator(func): argnameset = set(name.strip() for name in argnames.split(",")) def _wrapped(*args, **kwargs): vals = [kwargs.get(name) for name in argnameset] assert sum(val is not None for val in vals) == 1 return func(*args, **kwargs) return _wrapped return _decorator else: # pragma: not testing # We aren't using real PyContracts, so just define our decorators as # stunt-double no-ops. contract = dummy_decorator_with_args one_of = dummy_decorator_with_args def new_contract(*args_unused, **kwargs_unused): """Dummy no-op implementation of `new_contract`.""" pass def nice_pair(pair): """Make a nice string representation of a pair of numbers. If the numbers are equal, just return the number, otherwise return the pair with a dash between them, indicating the range. """ start, end = pair if start == end: return "%d" % start else: return "%d-%d" % (start, end) def format_lines(statements, lines): """Nicely format a list of line numbers. Format a list of line numbers for printing by coalescing groups of lines as long as the lines represent consecutive statements. This will coalesce even if there are gaps between statements. For example, if `statements` is [1,2,3,4,5,10,11,12,13,14] and `lines` is [1,2,5,10,11,13,14] then the result will be "1-2, 5-11, 13-14". Both `lines` and `statements` can be any iterable. All of the elements of `lines` must be in `statements`, and all of the values must be positive integers. """ statements = sorted(statements) lines = sorted(lines) pairs = [] start = None lidx = 0 for stmt in statements: if lidx >= len(lines): break if stmt == lines[lidx]: lidx += 1 if not start: start = stmt end = stmt elif start: pairs.append((start, end)) start = None if start: pairs.append((start, end)) ret = ', '.join(map(nice_pair, pairs)) return ret def expensive(fn): """A decorator to indicate that a method shouldn't be called more than once. Normally, this does nothing. During testing, this raises an exception if called more than once. """ if env.TESTING: attr = "_once_" + fn.__name__ def _wrapped(self): """Inner function that checks the cache.""" if hasattr(self, attr): raise AssertionError("Shouldn't have called %s more than once" % fn.__name__) setattr(self, attr, True) return fn(self) return _wrapped else: return fn # pragma: not testing def bool_or_none(b): """Return bool(b), but preserve None.""" if b is None: return None else: return bool(b) def join_regex(regexes): """Combine a list of regexes into one that matches any of them.""" return "|".join("(?:%s)" % r for r in regexes) def file_be_gone(path): """Remove a file, and don't get annoyed if it doesn't exist.""" try: os.remove(path) except OSError as e: if e.errno != errno.ENOENT: raise def output_encoding(outfile=None): """Determine the encoding to use for output written to `outfile` or stdout.""" if outfile is None: outfile = sys.stdout encoding = ( getattr(outfile, "encoding", None) or getattr(sys.__stdout__, "encoding", None) or locale.getpreferredencoding() ) return encoding class Hasher(object): """Hashes Python data into md5.""" def __init__(self): self.md5 = hashlib.md5() def update(self, v): """Add `v` to the hash, recursively if needed.""" self.md5.update(to_bytes(str(type(v)))) if isinstance(v, unicode_class): self.md5.update(v.encode('utf8')) elif isinstance(v, bytes): self.md5.update(v) elif v is None: pass elif isinstance(v, (int, float)): self.md5.update(to_bytes(str(v))) elif isinstance(v, (tuple, list)): for e in v: self.update(e) elif isinstance(v, dict): keys = v.keys() for k in sorted(keys): self.update(k) self.update(v[k]) else: for k in dir(v): if k.startswith('__'): continue a = getattr(v, k) if inspect.isroutine(a): continue self.update(k) self.update(a) def hexdigest(self): """Retrieve the hex digest of the hash.""" return self.md5.hexdigest() def _needs_to_implement(that, func_name): """Helper to raise NotImplementedError in interface stubs.""" if hasattr(that, "_coverage_plugin_name"): thing = "Plugin" name = that._coverage_plugin_name else: thing = "Class" klass = that.__class__ name = "{klass.__module__}.{klass.__name__}".format(klass=klass) raise NotImplementedError( "{thing} {name!r} needs to implement {func_name}()".format( thing=thing, name=name, func_name=func_name ) ) class SimpleRepr(object): """A mixin implementing a simple __repr__.""" def __repr__(self): return "<{klass} @{id:x} {attrs}>".format( klass=self.__class__.__name__, id=id(self) & 0xFFFFFF, attrs=" ".join("{}={!r}".format(k, v) for k, v in self.__dict__.items()), ) class BaseCoverageException(Exception): """The base of all Coverage exceptions.""" pass class CoverageException(BaseCoverageException): """A run-of-the-mill exception specific to coverage.py.""" pass class NoSource(CoverageException): """We couldn't find the source for a module.""" pass class NoCode(NoSource): """We couldn't find any code at all.""" pass class NotPython(CoverageException): """A source file turned out not to be parsable Python.""" pass class ExceptionDuringRun(CoverageException): """An exception happened while running customer code. Construct it with three arguments, the values from `sys.exc_info`. """ pass class StopEverything(BaseCoverageException): """An exception that means everything should stop. The CoverageTest class converts these to SkipTest, so that when running tests, raising this exception will automatically skip the test. """ pass python-coverage-4.5+dfsg.1.orig/coverage/__init__.py0000644000076600000620000000250113177624110020362 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Code coverage measurement for Python. Ned Batchelder https://nedbatchelder.com/code/coverage """ from coverage.version import __version__, __url__, version_info from coverage.control import Coverage, process_startup from coverage.data import CoverageData from coverage.debug import enable_aspectlib_maybe from coverage.misc import CoverageException from coverage.plugin import CoveragePlugin, FileTracer, FileReporter from coverage.pytracer import PyTracer # Backward compatibility. coverage = Coverage # Possibly enable aspectlib to debug our execution. enable_aspectlib_maybe() # On Windows, we encode and decode deep enough that something goes wrong and # the encodings.utf_8 module is loaded and then unloaded, I don't know why. # Adding a reference here prevents it from being unloaded. Yuk. import encodings.utf_8 # Because of the "from coverage.control import fooey" lines at the top of the # file, there's an entry for coverage.coverage in sys.modules, mapped to None. # This makes some inspection tools (like pydoc) unable to find the class # coverage.coverage. So remove that entry. import sys try: del sys.modules['coverage.coverage'] except KeyError: pass python-coverage-4.5+dfsg.1.orig/coverage/pickle2json.py0000644000076600000620000000272113146037571021057 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Convert pickle to JSON for coverage.py.""" from coverage.backward import pickle from coverage.data import CoverageData def pickle_read_raw_data(cls_unused, file_obj): """Replacement for CoverageData._read_raw_data.""" return pickle.load(file_obj) def pickle2json(infile, outfile): """Convert a coverage.py 3.x pickle data file to a 4.x JSON data file.""" try: old_read_raw_data = CoverageData._read_raw_data CoverageData._read_raw_data = pickle_read_raw_data covdata = CoverageData() with open(infile, 'rb') as inf: covdata.read_fileobj(inf) covdata.write_file(outfile) finally: CoverageData._read_raw_data = old_read_raw_data if __name__ == "__main__": from optparse import OptionParser parser = OptionParser(usage="usage: %s [options]" % __file__) parser.description = "Convert .coverage files from pickle to JSON format" parser.add_option( "-i", "--input-file", action="store", default=".coverage", help="Name of input file. Default .coverage", ) parser.add_option( "-o", "--output-file", action="store", default=".coverage", help="Name of output file. Default .coverage", ) (options, args) = parser.parse_args() pickle2json(options.input_file, options.output_file) python-coverage-4.5+dfsg.1.orig/coverage/execfile.py0000644000076600000620000002347313226526711020425 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Execute files of Python code.""" import marshal import os import struct import sys import types from coverage.backward import BUILTINS from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec from coverage.misc import CoverageException, ExceptionDuringRun, NoCode, NoSource, isolate_module from coverage.phystokens import compile_unicode from coverage.python import get_python_source os = isolate_module(os) class DummyLoader(object): """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader. Currently only implements the .fullname attribute """ def __init__(self, fullname, *_args): self.fullname = fullname if importlib_util_find_spec: def find_module(modulename): """Find the module named `modulename`. Returns the file path of the module, and the name of the enclosing package. """ try: spec = importlib_util_find_spec(modulename) except ImportError as err: raise NoSource(str(err)) if not spec: raise NoSource("No module named %r" % (modulename,)) pathname = spec.origin packagename = spec.name if pathname.endswith("__init__.py") and not modulename.endswith("__init__"): mod_main = modulename + ".__main__" spec = importlib_util_find_spec(mod_main) if not spec: raise NoSource( "No module named %s; " "%r is a package and cannot be directly executed" % (mod_main, modulename) ) pathname = spec.origin packagename = spec.name packagename = packagename.rpartition(".")[0] return pathname, packagename else: def find_module(modulename): """Find the module named `modulename`. Returns the file path of the module, and the name of the enclosing package. """ openfile = None glo, loc = globals(), locals() try: # Search for the module - inside its parent package, if any - using # standard import mechanics. if '.' in modulename: packagename, name = modulename.rsplit('.', 1) package = __import__(packagename, glo, loc, ['__path__']) searchpath = package.__path__ else: packagename, name = None, modulename searchpath = None # "top-level search" in imp.find_module() openfile, pathname, _ = imp.find_module(name, searchpath) # Complain if this is a magic non-file module. if openfile is None and pathname is None: raise NoSource( "module does not live in a file: %r" % modulename ) # If `modulename` is actually a package, not a mere module, then we # pretend to be Python 2.7 and try running its __main__.py script. if openfile is None: packagename = modulename name = '__main__' package = __import__(packagename, glo, loc, ['__path__']) searchpath = package.__path__ openfile, pathname, _ = imp.find_module(name, searchpath) except ImportError as err: raise NoSource(str(err)) finally: if openfile: openfile.close() return pathname, packagename def run_python_module(modulename, args): """Run a Python module, as though with ``python -m name args...``. `modulename` is the name of the module, possibly a dot-separated name. `args` is the argument array to present as sys.argv, including the first element naming the module being executed. """ pathname, packagename = find_module(modulename) pathname = os.path.abspath(pathname) args[0] = pathname run_python_file(pathname, args, package=packagename, modulename=modulename, path0="") def run_python_file(filename, args, package=None, modulename=None, path0=None): """Run a Python file as if it were the main program on the command line. `filename` is the path to the file to execute, it need not be a .py file. `args` is the argument array to present as sys.argv, including the first element naming the file being executed. `package` is the name of the enclosing package, if any. `modulename` is the name of the module the file was run as. `path0` is the value to put into sys.path[0]. If it's None, then this function will decide on a value. """ if modulename is None and sys.version_info >= (3, 3): modulename = '__main__' # Create a module to serve as __main__ old_main_mod = sys.modules['__main__'] main_mod = types.ModuleType('__main__') sys.modules['__main__'] = main_mod main_mod.__file__ = filename if package: main_mod.__package__ = package if modulename: main_mod.__loader__ = DummyLoader(modulename) main_mod.__builtins__ = BUILTINS # Set sys.argv properly. old_argv = sys.argv sys.argv = args if os.path.isdir(filename): # Running a directory means running the __main__.py file in that # directory. my_path0 = filename for ext in [".py", ".pyc", ".pyo"]: try_filename = os.path.join(filename, "__main__" + ext) if os.path.exists(try_filename): filename = try_filename break else: raise NoSource("Can't find '__main__' module in '%s'" % filename) else: my_path0 = os.path.abspath(os.path.dirname(filename)) # Set sys.path correctly. old_path0 = sys.path[0] sys.path[0] = path0 if path0 is not None else my_path0 try: try: # Make a code object somehow. if filename.endswith((".pyc", ".pyo")): code = make_code_from_pyc(filename) else: code = make_code_from_py(filename) except CoverageException: raise except Exception as exc: msg = "Couldn't run {filename!r} as Python code: {exc.__class__.__name__}: {exc}" raise CoverageException(msg.format(filename=filename, exc=exc)) # Execute the code object. try: exec(code, main_mod.__dict__) except SystemExit: # The user called sys.exit(). Just pass it along to the upper # layers, where it will be handled. raise except Exception: # Something went wrong while executing the user code. # Get the exc_info, and pack them into an exception that we can # throw up to the outer loop. We peel one layer off the traceback # so that the coverage.py code doesn't appear in the final printed # traceback. typ, err, tb = sys.exc_info() # PyPy3 weirdness. If I don't access __context__, then somehow it # is non-None when the exception is reported at the upper layer, # and a nested exception is shown to the user. This getattr fixes # it somehow? https://bitbucket.org/pypy/pypy/issue/1903 getattr(err, '__context__', None) # Call the excepthook. try: if hasattr(err, "__traceback__"): err.__traceback__ = err.__traceback__.tb_next sys.excepthook(typ, err, tb.tb_next) except SystemExit: raise except Exception: # Getting the output right in the case of excepthook # shenanigans is kind of involved. sys.stderr.write("Error in sys.excepthook:\n") typ2, err2, tb2 = sys.exc_info() err2.__suppress_context__ = True if hasattr(err2, "__traceback__"): err2.__traceback__ = err2.__traceback__.tb_next sys.__excepthook__(typ2, err2, tb2.tb_next) sys.stderr.write("\nOriginal exception was:\n") raise ExceptionDuringRun(typ, err, tb.tb_next) else: sys.exit(1) finally: # Restore the old __main__, argv, and path. sys.modules['__main__'] = old_main_mod sys.argv = old_argv sys.path[0] = old_path0 def make_code_from_py(filename): """Get source from `filename` and make a code object of it.""" # Open the source file. try: source = get_python_source(filename) except (IOError, NoSource): raise NoSource("No file to run: '%s'" % filename) code = compile_unicode(source, filename, "exec") return code def make_code_from_pyc(filename): """Get a code object from a .pyc file.""" try: fpyc = open(filename, "rb") except IOError: raise NoCode("No file to run: '%s'" % filename) with fpyc: # First four bytes are a version-specific magic number. It has to # match or we won't run the file. magic = fpyc.read(4) if magic != PYC_MAGIC_NUMBER: raise NoCode("Bad magic number in .pyc file") date_based = True if sys.version_info >= (3, 7, 0, 'alpha', 4): flags = struct.unpack('= (3, 3): # 3.3 added another long to the header (size), skip it. fpyc.read(4) # The rest of the file is the code object we want. code = marshal.load(fpyc) return code python-coverage-4.5+dfsg.1.orig/coverage/xmlreport.py0000644000076600000620000002014013173515667020672 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """XML reporting for coverage.py""" import os import os.path import sys import time import xml.dom.minidom from coverage import env from coverage import __url__, __version__, files from coverage.backward import iitems from coverage.misc import isolate_module from coverage.report import Reporter os = isolate_module(os) DTD_URL = 'https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd' def rate(hit, num): """Return the fraction of `hit`/`num`, as a string.""" if num == 0: return "1" else: return "%.4g" % (float(hit) / num) class XmlReporter(Reporter): """A reporter for writing Cobertura-style XML coverage results.""" def __init__(self, coverage, config): super(XmlReporter, self).__init__(coverage, config) self.source_paths = set() if config.source: for src in config.source: if os.path.exists(src): self.source_paths.add(files.canonical_filename(src)) self.packages = {} self.xml_out = None self.has_arcs = coverage.data.has_arcs() def report(self, morfs, outfile=None): """Generate a Cobertura-compatible XML report for `morfs`. `morfs` is a list of modules or file names. `outfile` is a file object to write the XML to. """ # Initial setup. outfile = outfile or sys.stdout # Create the DOM that will store the data. impl = xml.dom.minidom.getDOMImplementation() self.xml_out = impl.createDocument(None, "coverage", None) # Write header stuff. xcoverage = self.xml_out.documentElement xcoverage.setAttribute("version", __version__) xcoverage.setAttribute("timestamp", str(int(time.time()*1000))) xcoverage.appendChild(self.xml_out.createComment( " Generated by coverage.py: %s " % __url__ )) xcoverage.appendChild(self.xml_out.createComment(" Based on %s " % DTD_URL)) # Call xml_file for each file in the data. self.report_files(self.xml_file, morfs) xsources = self.xml_out.createElement("sources") xcoverage.appendChild(xsources) # Populate the XML DOM with the source info. for path in sorted(self.source_paths): xsource = self.xml_out.createElement("source") xsources.appendChild(xsource) txt = self.xml_out.createTextNode(path) xsource.appendChild(txt) lnum_tot, lhits_tot = 0, 0 bnum_tot, bhits_tot = 0, 0 xpackages = self.xml_out.createElement("packages") xcoverage.appendChild(xpackages) # Populate the XML DOM with the package info. for pkg_name, pkg_data in sorted(iitems(self.packages)): class_elts, lhits, lnum, bhits, bnum = pkg_data xpackage = self.xml_out.createElement("package") xpackages.appendChild(xpackage) xclasses = self.xml_out.createElement("classes") xpackage.appendChild(xclasses) for _, class_elt in sorted(iitems(class_elts)): xclasses.appendChild(class_elt) xpackage.setAttribute("name", pkg_name.replace(os.sep, '.')) xpackage.setAttribute("line-rate", rate(lhits, lnum)) if self.has_arcs: branch_rate = rate(bhits, bnum) else: branch_rate = "0" xpackage.setAttribute("branch-rate", branch_rate) xpackage.setAttribute("complexity", "0") lnum_tot += lnum lhits_tot += lhits bnum_tot += bnum bhits_tot += bhits xcoverage.setAttribute("lines-valid", str(lnum_tot)) xcoverage.setAttribute("lines-covered", str(lhits_tot)) xcoverage.setAttribute("line-rate", rate(lhits_tot, lnum_tot)) if self.has_arcs: xcoverage.setAttribute("branches-valid", str(bnum_tot)) xcoverage.setAttribute("branches-covered", str(bhits_tot)) xcoverage.setAttribute("branch-rate", rate(bhits_tot, bnum_tot)) else: xcoverage.setAttribute("branches-covered", "0") xcoverage.setAttribute("branches-valid", "0") xcoverage.setAttribute("branch-rate", "0") xcoverage.setAttribute("complexity", "0") # Use the DOM to write the output file. out = self.xml_out.toprettyxml() if env.PY2: out = out.encode("utf8") outfile.write(out) # Return the total percentage. denom = lnum_tot + bnum_tot if denom == 0: pct = 0.0 else: pct = 100.0 * (lhits_tot + bhits_tot) / denom return pct def xml_file(self, fr, analysis): """Add to the XML report for a single file.""" # Create the 'lines' and 'package' XML elements, which # are populated later. Note that a package == a directory. filename = fr.filename.replace("\\", "/") for source_path in self.source_paths: if filename.startswith(source_path.replace("\\", "/") + "/"): rel_name = filename[len(source_path)+1:] break else: rel_name = fr.relative_filename() dirname = os.path.dirname(rel_name) or u"." dirname = "/".join(dirname.split("/")[:self.config.xml_package_depth]) package_name = dirname.replace("/", ".") if rel_name != fr.filename: self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r"\/")) package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0]) xclass = self.xml_out.createElement("class") xclass.appendChild(self.xml_out.createElement("methods")) xlines = self.xml_out.createElement("lines") xclass.appendChild(xlines) xclass.setAttribute("name", os.path.relpath(rel_name, dirname)) xclass.setAttribute("filename", rel_name.replace("\\", "/")) xclass.setAttribute("complexity", "0") branch_stats = analysis.branch_stats() missing_branch_arcs = analysis.missing_branch_arcs() # For each statement, create an XML 'line' element. for line in sorted(analysis.statements): xline = self.xml_out.createElement("line") xline.setAttribute("number", str(line)) # Q: can we get info about the number of times a statement is # executed? If so, that should be recorded here. xline.setAttribute("hits", str(int(line not in analysis.missing))) if self.has_arcs: if line in branch_stats: total, taken = branch_stats[line] xline.setAttribute("branch", "true") xline.setAttribute( "condition-coverage", "%d%% (%d/%d)" % (100*taken//total, taken, total) ) if line in missing_branch_arcs: annlines = ["exit" if b < 0 else str(b) for b in missing_branch_arcs[line]] xline.setAttribute("missing-branches", ",".join(annlines)) xlines.appendChild(xline) class_lines = len(analysis.statements) class_hits = class_lines - len(analysis.missing) if self.has_arcs: class_branches = sum(t for t, k in branch_stats.values()) missing_branches = sum(t - k for t, k in branch_stats.values()) class_br_hits = class_branches - missing_branches else: class_branches = 0.0 class_br_hits = 0.0 # Finalize the statistics that are collected in the XML DOM. xclass.setAttribute("line-rate", rate(class_hits, class_lines)) if self.has_arcs: branch_rate = rate(class_br_hits, class_branches) else: branch_rate = "0" xclass.setAttribute("branch-rate", branch_rate) package[0][rel_name] = xclass package[1] += class_hits package[2] += class_lines package[3] += class_br_hits package[4] += class_branches python-coverage-4.5+dfsg.1.orig/coverage/bytecode.py0000644000076600000620000000132313146037571020427 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Bytecode manipulation for coverage.py""" import types class CodeObjects(object): """Iterate over all the code objects in `code`.""" def __init__(self, code): self.stack = [code] def __iter__(self): while self.stack: # We're going to return the code object on the stack, but first # push its children for later returning. code = self.stack.pop() for c in code.co_consts: if isinstance(c, types.CodeType): self.stack.append(c) yield code python-coverage-4.5+dfsg.1.orig/coverage/cmdline.py0000644000076600000620000006252513231120740020241 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Command-line support for coverage.py.""" from __future__ import print_function import glob import optparse import os.path import sys import textwrap import traceback from coverage import env from coverage.collector import CTracer from coverage.debug import info_formatter, info_header from coverage.execfile import run_python_file, run_python_module from coverage.misc import BaseCoverageException, ExceptionDuringRun, NoSource from coverage.results import should_fail_under class Opts(object): """A namespace class for individual options we'll build parsers from.""" append = optparse.make_option( '-a', '--append', action='store_true', help="Append coverage data to .coverage, otherwise it starts clean each time.", ) branch = optparse.make_option( '', '--branch', action='store_true', help="Measure branch coverage in addition to statement coverage.", ) CONCURRENCY_CHOICES = [ "thread", "gevent", "greenlet", "eventlet", "multiprocessing", ] concurrency = optparse.make_option( '', '--concurrency', action='store', metavar="LIB", choices=CONCURRENCY_CHOICES, help=( "Properly measure code using a concurrency library. " "Valid values are: %s." ) % ", ".join(CONCURRENCY_CHOICES), ) debug = optparse.make_option( '', '--debug', action='store', metavar="OPTS", help="Debug options, separated by commas", ) directory = optparse.make_option( '-d', '--directory', action='store', metavar="DIR", help="Write the output files to DIR.", ) fail_under = optparse.make_option( '', '--fail-under', action='store', metavar="MIN", type="float", help="Exit with a status of 2 if the total coverage is less than MIN.", ) help = optparse.make_option( '-h', '--help', action='store_true', help="Get help on this command.", ) ignore_errors = optparse.make_option( '-i', '--ignore-errors', action='store_true', help="Ignore errors while reading source files.", ) include = optparse.make_option( '', '--include', action='store', metavar="PAT1,PAT2,...", help=( "Include only files whose paths match one of these patterns. " "Accepts shell-style wildcards, which must be quoted." ), ) pylib = optparse.make_option( '-L', '--pylib', action='store_true', help=( "Measure coverage even inside the Python installed library, " "which isn't done by default." ), ) show_missing = optparse.make_option( '-m', '--show-missing', action='store_true', help="Show line numbers of statements in each module that weren't executed.", ) skip_covered = optparse.make_option( '--skip-covered', action='store_true', help="Skip files with 100% coverage.", ) omit = optparse.make_option( '', '--omit', action='store', metavar="PAT1,PAT2,...", help=( "Omit files whose paths match one of these patterns. " "Accepts shell-style wildcards, which must be quoted." ), ) output_xml = optparse.make_option( '-o', '', action='store', dest="outfile", metavar="OUTFILE", help="Write the XML report to this file. Defaults to 'coverage.xml'", ) parallel_mode = optparse.make_option( '-p', '--parallel-mode', action='store_true', help=( "Append the machine name, process id and random number to the " ".coverage data file name to simplify collecting data from " "many processes." ), ) module = optparse.make_option( '-m', '--module', action='store_true', help=( " is an importable Python module, not a script path, " "to be run as 'python -m' would run it." ), ) rcfile = optparse.make_option( '', '--rcfile', action='store', help="Specify configuration file. Defaults to '.coveragerc'", ) source = optparse.make_option( '', '--source', action='store', metavar="SRC1,SRC2,...", help="A list of packages or directories of code to be measured.", ) timid = optparse.make_option( '', '--timid', action='store_true', help=( "Use a simpler but slower trace method. Try this if you get " "seemingly impossible results!" ), ) title = optparse.make_option( '', '--title', action='store', metavar="TITLE", help="A text string to use as the title on the HTML.", ) version = optparse.make_option( '', '--version', action='store_true', help="Display version information and exit.", ) class CoverageOptionParser(optparse.OptionParser, object): """Base OptionParser for coverage.py. Problems don't exit the program. Defaults are initialized for all options. """ def __init__(self, *args, **kwargs): super(CoverageOptionParser, self).__init__( add_help_option=False, *args, **kwargs ) self.set_defaults( action=None, append=None, branch=None, concurrency=None, debug=None, directory=None, fail_under=None, help=None, ignore_errors=None, include=None, module=None, omit=None, parallel_mode=None, pylib=None, rcfile=True, show_missing=None, skip_covered=None, source=None, timid=None, title=None, version=None, ) self.disable_interspersed_args() self.help_fn = self.help_noop def help_noop(self, error=None, topic=None, parser=None): """No-op help function.""" pass class OptionParserError(Exception): """Used to stop the optparse error handler ending the process.""" pass def parse_args_ok(self, args=None, options=None): """Call optparse.parse_args, but return a triple: (ok, options, args) """ try: options, args = \ super(CoverageOptionParser, self).parse_args(args, options) except self.OptionParserError: return False, None, None return True, options, args def error(self, msg): """Override optparse.error so sys.exit doesn't get called.""" self.help_fn(msg) raise self.OptionParserError class GlobalOptionParser(CoverageOptionParser): """Command-line parser for coverage.py global option arguments.""" def __init__(self): super(GlobalOptionParser, self).__init__() self.add_options([ Opts.help, Opts.version, ]) class CmdOptionParser(CoverageOptionParser): """Parse one of the new-style commands for coverage.py.""" def __init__(self, action, options, defaults=None, usage=None, description=None): """Create an OptionParser for a coverage.py command. `action` is the slug to put into `options.action`. `options` is a list of Option's for the command. `defaults` is a dict of default value for options. `usage` is the usage string to display in help. `description` is the description of the command, for the help text. """ if usage: usage = "%prog " + usage super(CmdOptionParser, self).__init__( usage=usage, description=description, ) self.set_defaults(action=action, **(defaults or {})) self.add_options(options) self.cmd = action def __eq__(self, other): # A convenience equality, so that I can put strings in unit test # results, and they will compare equal to objects. return (other == "" % self.cmd) __hash__ = None # This object doesn't need to be hashed. def get_prog_name(self): """Override of an undocumented function in optparse.OptionParser.""" program_name = super(CmdOptionParser, self).get_prog_name() # Include the sub-command for this parser as part of the command. return "{command} {subcommand}".format(command=program_name, subcommand=self.cmd) GLOBAL_ARGS = [ Opts.debug, Opts.help, Opts.rcfile, ] CMDS = { 'annotate': CmdOptionParser( "annotate", [ Opts.directory, Opts.ignore_errors, Opts.include, Opts.omit, ] + GLOBAL_ARGS, usage="[options] [modules]", description=( "Make annotated copies of the given files, marking statements that are executed " "with > and statements that are missed with !." ), ), 'combine': CmdOptionParser( "combine", [ Opts.append, ] + GLOBAL_ARGS, usage="[options] ... ", description=( "Combine data from multiple coverage files collected " "with 'run -p'. The combined results are written to a single " "file representing the union of the data. The positional " "arguments are data files or directories containing data files. " "If no paths are provided, data files in the default data file's " "directory are combined." ), ), 'debug': CmdOptionParser( "debug", GLOBAL_ARGS, usage="", description=( "Display information on the internals of coverage.py, " "for diagnosing problems. " "Topics are 'data' to show a summary of the collected data, " "or 'sys' to show installation information." ), ), 'erase': CmdOptionParser( "erase", GLOBAL_ARGS, description="Erase previously collected coverage data.", ), 'help': CmdOptionParser( "help", GLOBAL_ARGS, usage="[command]", description="Describe how to use coverage.py", ), 'html': CmdOptionParser( "html", [ Opts.directory, Opts.fail_under, Opts.ignore_errors, Opts.include, Opts.omit, Opts.title, Opts.skip_covered, ] + GLOBAL_ARGS, usage="[options] [modules]", description=( "Create an HTML report of the coverage of the files. " "Each file gets its own page, with the source decorated to show " "executed, excluded, and missed lines." ), ), 'report': CmdOptionParser( "report", [ Opts.fail_under, Opts.ignore_errors, Opts.include, Opts.omit, Opts.show_missing, Opts.skip_covered, ] + GLOBAL_ARGS, usage="[options] [modules]", description="Report coverage statistics on modules." ), 'run': CmdOptionParser( "run", [ Opts.append, Opts.branch, Opts.concurrency, Opts.include, Opts.module, Opts.omit, Opts.pylib, Opts.parallel_mode, Opts.source, Opts.timid, ] + GLOBAL_ARGS, usage="[options] [program options]", description="Run a Python program, measuring code execution." ), 'xml': CmdOptionParser( "xml", [ Opts.fail_under, Opts.ignore_errors, Opts.include, Opts.omit, Opts.output_xml, ] + GLOBAL_ARGS, usage="[options] [modules]", description="Generate an XML report of coverage results." ), } OK, ERR, FAIL_UNDER = 0, 1, 2 class CoverageScript(object): """The command-line interface to coverage.py.""" def __init__(self, _covpkg=None, _run_python_file=None, _run_python_module=None, _help_fn=None, _path_exists=None): # _covpkg is for dependency injection, so we can test this code. if _covpkg: self.covpkg = _covpkg else: import coverage self.covpkg = coverage # For dependency injection: self.run_python_file = _run_python_file or run_python_file self.run_python_module = _run_python_module or run_python_module self.help_fn = _help_fn or self.help self.path_exists = _path_exists or os.path.exists self.global_option = False self.coverage = None program_path = sys.argv[0] if program_path.endswith(os.path.sep + '__main__.py'): # The path is the main module of a package; get that path instead. program_path = os.path.dirname(program_path) self.program_name = os.path.basename(program_path) if env.WINDOWS: # entry_points={'console_scripts':...} on Windows makes files # called coverage.exe, coverage3.exe, and coverage-3.5.exe. These # invoke coverage-script.py, coverage3-script.py, and # coverage-3.5-script.py. argv[0] is the .py file, but we want to # get back to the original form. auto_suffix = "-script.py" if self.program_name.endswith(auto_suffix): self.program_name = self.program_name[:-len(auto_suffix)] def command_line(self, argv): """The bulk of the command line interface to coverage.py. `argv` is the argument list to process. Returns 0 if all is well, 1 if something went wrong. """ # Collect the command-line options. if not argv: self.help_fn(topic='minimum_help') return OK # The command syntax we parse depends on the first argument. Global # switch syntax always starts with an option. self.global_option = argv[0].startswith('-') if self.global_option: parser = GlobalOptionParser() else: parser = CMDS.get(argv[0]) if not parser: self.help_fn("Unknown command: '%s'" % argv[0]) return ERR argv = argv[1:] parser.help_fn = self.help_fn ok, options, args = parser.parse_args_ok(argv) if not ok: return ERR # Handle help and version. if self.do_help(options, args, parser): return OK # We need to be able to import from the current directory, because # plugins may try to, for example, to read Django settings. sys.path[0] = '' # Listify the list options. source = unshell_list(options.source) omit = unshell_list(options.omit) include = unshell_list(options.include) debug = unshell_list(options.debug) # Do something. self.coverage = self.covpkg.Coverage( data_suffix=options.parallel_mode, cover_pylib=options.pylib, timid=options.timid, branch=options.branch, config_file=options.rcfile, source=source, omit=omit, include=include, debug=debug, concurrency=options.concurrency, ) if options.action == "debug": return self.do_debug(args) elif options.action == "erase": self.coverage.erase() return OK elif options.action == "run": return self.do_run(options, args) elif options.action == "combine": if options.append: self.coverage.load() data_dirs = args or None self.coverage.combine(data_dirs, strict=True) self.coverage.save() return OK # Remaining actions are reporting, with some common options. report_args = dict( morfs=unglob_args(args), ignore_errors=options.ignore_errors, omit=omit, include=include, ) self.coverage.load() total = None if options.action == "report": total = self.coverage.report( show_missing=options.show_missing, skip_covered=options.skip_covered, **report_args) elif options.action == "annotate": self.coverage.annotate( directory=options.directory, **report_args) elif options.action == "html": total = self.coverage.html_report( directory=options.directory, title=options.title, skip_covered=options.skip_covered, **report_args) elif options.action == "xml": outfile = options.outfile total = self.coverage.xml_report(outfile=outfile, **report_args) if total is not None: # Apply the command line fail-under options, and then use the config # value, so we can get fail_under from the config file. if options.fail_under is not None: self.coverage.set_option("report:fail_under", options.fail_under) fail_under = self.coverage.get_option("report:fail_under") precision = self.coverage.get_option("report:precision") if should_fail_under(total, fail_under, precision): return FAIL_UNDER return OK def help(self, error=None, topic=None, parser=None): """Display an error message, or the named topic.""" assert error or topic or parser if error: print(error, file=sys.stderr) print("Use '%s help' for help." % (self.program_name,), file=sys.stderr) elif parser: print(parser.format_help().strip()) else: help_params = dict(self.covpkg.__dict__) help_params['program_name'] = self.program_name if CTracer is not None: help_params['extension_modifier'] = 'with C extension' else: help_params['extension_modifier'] = 'without C extension' help_msg = textwrap.dedent(HELP_TOPICS.get(topic, '')).strip() if help_msg: print(help_msg.format(**help_params)) else: print("Don't know topic %r" % topic) def do_help(self, options, args, parser): """Deal with help requests. Return True if it handled the request, False if not. """ # Handle help. if options.help: if self.global_option: self.help_fn(topic='help') else: self.help_fn(parser=parser) return True if options.action == "help": if args: for a in args: parser = CMDS.get(a) if parser: self.help_fn(parser=parser) else: self.help_fn(topic=a) else: self.help_fn(topic='help') return True # Handle version. if options.version: self.help_fn(topic='version') return True return False def do_run(self, options, args): """Implementation of 'coverage run'.""" if not args: self.help_fn("Nothing to do.") return ERR if options.append and self.coverage.get_option("run:parallel"): self.help_fn("Can't append to data files in parallel mode.") return ERR if options.concurrency == "multiprocessing": # Can't set other run-affecting command line options with # multiprocessing. for opt_name in ['branch', 'include', 'omit', 'pylib', 'source', 'timid']: # As it happens, all of these options have no default, meaning # they will be None if they have not been specified. if getattr(options, opt_name) is not None: self.help_fn( "Options affecting multiprocessing must be specified " "in a configuration file." ) return ERR if not self.coverage.get_option("run:parallel"): if not options.append: self.coverage.erase() # Run the script. self.coverage.start() code_ran = True try: if options.module: self.run_python_module(args[0], args) else: filename = args[0] self.run_python_file(filename, args) except NoSource: code_ran = False raise finally: self.coverage.stop() if code_ran: if options.append: data_file = self.coverage.get_option("run:data_file") if self.path_exists(data_file): self.coverage.combine(data_paths=[data_file]) self.coverage.save() return OK def do_debug(self, args): """Implementation of 'coverage debug'.""" if not args: self.help_fn("What information would you like: config, data, sys?") return ERR for info in args: if info == 'sys': sys_info = self.coverage.sys_info() print(info_header("sys")) for line in info_formatter(sys_info): print(" %s" % line) elif info == 'data': self.coverage.load() data = self.coverage.data print(info_header("data")) print("path: %s" % self.coverage.data_files.filename) if data: print("has_arcs: %r" % data.has_arcs()) summary = data.line_counts(fullpath=True) filenames = sorted(summary.keys()) print("\n%d files:" % len(filenames)) for f in filenames: line = "%s: %d lines" % (f, summary[f]) plugin = data.file_tracer(f) if plugin: line += " [%s]" % plugin print(line) else: print("No data collected") elif info == 'config': print(info_header("config")) config_info = self.coverage.config.__dict__.items() for line in info_formatter(config_info): print(" %s" % line) else: self.help_fn("Don't know what you mean by %r" % info) return ERR return OK def unshell_list(s): """Turn a command-line argument into a list.""" if not s: return None if env.WINDOWS: # When running coverage.py as coverage.exe, some of the behavior # of the shell is emulated: wildcards are expanded into a list of # file names. So you have to single-quote patterns on the command # line, but (not) helpfully, the single quotes are included in the # argument, so we have to strip them off here. s = s.strip("'") return s.split(',') def unglob_args(args): """Interpret shell wildcards for platforms that need it.""" if env.WINDOWS: globbed = [] for arg in args: if '?' in arg or '*' in arg: globbed.extend(glob.glob(arg)) else: globbed.append(arg) args = globbed return args HELP_TOPICS = { 'help': """\ Coverage.py, version {__version__} {extension_modifier} Measure, collect, and report on code coverage in Python programs. usage: {program_name} [options] [args] Commands: annotate Annotate source files with execution information. combine Combine a number of data files. erase Erase previously collected coverage data. help Get help on using coverage.py. html Create an HTML report. report Report coverage stats on modules. run Run a Python program and measure code execution. xml Create an XML report of coverage results. Use "{program_name} help " for detailed help on any command. For full documentation, see {__url__} """, 'minimum_help': """\ Code coverage for Python. Use '{program_name} help' for help. """, 'version': """\ Coverage.py, version {__version__} {extension_modifier} Documentation at {__url__} """, } def main(argv=None): """The main entry point to coverage.py. This is installed as the script entry point. """ if argv is None: argv = sys.argv[1:] try: status = CoverageScript().command_line(argv) except ExceptionDuringRun as err: # An exception was caught while running the product code. The # sys.exc_info() return tuple is packed into an ExceptionDuringRun # exception. traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter status = ERR except BaseCoverageException as err: # A controlled error inside coverage.py: print the message to the user. print(err) status = ERR except SystemExit as err: # The user called `sys.exit()`. Exit with their argument, if any. if err.args: status = err.args[0] else: status = None return status python-coverage-4.5+dfsg.1.orig/coverage/fullcoverage/0000755000076600000620000000000013235414514020731 5ustar staffpython-coverage-4.5+dfsg.1.orig/coverage/fullcoverage/encodings.py0000644000076600000620000000476413146037571023274 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Imposter encodings module that installs a coverage-style tracer. This is NOT the encodings module; it is an imposter that sets up tracing instrumentation and then replaces itself with the real encodings module. If the directory that holds this file is placed first in the PYTHONPATH when using "coverage" to run Python's tests, then this file will become the very first module imported by the internals of Python 3. It installs a coverage.py-compatible trace function that can watch Standard Library modules execute from the very earliest stages of Python's own boot process. This fixes a problem with coverage.py - that it starts too late to trace the coverage of many of the most fundamental modules in the Standard Library. """ import sys class FullCoverageTracer(object): def __init__(self): # `traces` is a list of trace events. Frames are tricky: the same # frame object is used for a whole scope, with new line numbers # written into it. So in one scope, all the frame objects are the # same object, and will eventually all will point to the last line # executed. So we keep the line numbers alongside the frames. # The list looks like: # # traces = [ # ((frame, event, arg), lineno), ... # ] # self.traces = [] def fullcoverage_trace(self, *args): frame, event, arg = args self.traces.append((args, frame.f_lineno)) return self.fullcoverage_trace sys.settrace(FullCoverageTracer().fullcoverage_trace) # In coverage/files.py is actual_filename(), which uses glob.glob. I don't # understand why, but that use of glob borks everything if fullcoverage is in # effect. So here we make an ugly hail-mary pass to switch off glob.glob over # there. This means when using fullcoverage, Windows path names will not be # their actual case. #sys.fullcoverage = True # Finally, remove our own directory from sys.path; remove ourselves from # sys.modules; and re-import "encodings", which will be the real package # this time. Note that the delete from sys.modules dictionary has to # happen last, since all of the symbols in this module will become None # at that exact moment, including "sys". parentdir = max(filter(__file__.startswith, sys.path), key=len) sys.path.remove(parentdir) del sys.modules['encodings'] import encodings python-coverage-4.5+dfsg.1.orig/coverage/results.py0000644000076600000620000002375613231146406020341 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Results of coverage measurement.""" import collections from coverage.backward import iitems from coverage.misc import contract, format_lines, SimpleRepr class Analysis(object): """The results of analyzing a FileReporter.""" def __init__(self, data, file_reporter): self.data = data self.file_reporter = file_reporter self.filename = self.file_reporter.filename self.statements = self.file_reporter.lines() self.excluded = self.file_reporter.excluded_lines() # Identify missing statements. executed = self.data.lines(self.filename) or [] executed = self.file_reporter.translate_lines(executed) self.missing = self.statements - executed if self.data.has_arcs(): self._arc_possibilities = sorted(self.file_reporter.arcs()) self.exit_counts = self.file_reporter.exit_counts() self.no_branch = self.file_reporter.no_branch_lines() n_branches = self.total_branches() mba = self.missing_branch_arcs() n_partial_branches = sum(len(v) for k,v in iitems(mba) if k not in self.missing) n_missing_branches = sum(len(v) for k,v in iitems(mba)) else: self._arc_possibilities = [] self.exit_counts = {} self.no_branch = set() n_branches = n_partial_branches = n_missing_branches = 0 self.numbers = Numbers( n_files=1, n_statements=len(self.statements), n_excluded=len(self.excluded), n_missing=len(self.missing), n_branches=n_branches, n_partial_branches=n_partial_branches, n_missing_branches=n_missing_branches, ) def missing_formatted(self): """The missing line numbers, formatted nicely. Returns a string like "1-2, 5-11, 13-14". """ return format_lines(self.statements, self.missing) def has_arcs(self): """Were arcs measured in this result?""" return self.data.has_arcs() def arc_possibilities(self): """Returns a sorted list of the arcs in the code.""" return self._arc_possibilities def arcs_executed(self): """Returns a sorted list of the arcs actually executed in the code.""" executed = self.data.arcs(self.filename) or [] executed = self.file_reporter.translate_arcs(executed) return sorted(executed) def arcs_missing(self): """Returns a sorted list of the arcs in the code not executed.""" possible = self.arc_possibilities() executed = self.arcs_executed() missing = ( p for p in possible if p not in executed and p[0] not in self.no_branch ) return sorted(missing) def arcs_missing_formatted(self): """The missing branch arcs, formatted nicely. Returns a string like "1->2, 1->3, 16->20". Omits any mention of branches from missing lines, so if line 17 is missing, then 17->18 won't be included. """ arcs = self.missing_branch_arcs() missing = self.missing line_exits = sorted(iitems(arcs)) pairs = [] for line, exits in line_exits: for ex in sorted(exits): if line not in missing: pairs.append("%d->%s" % (line, (ex if ex > 0 else "exit"))) return ', '.join(pairs) def arcs_unpredicted(self): """Returns a sorted list of the executed arcs missing from the code.""" possible = self.arc_possibilities() executed = self.arcs_executed() # Exclude arcs here which connect a line to itself. They can occur # in executed data in some cases. This is where they can cause # trouble, and here is where it's the least burden to remove them. # Also, generators can somehow cause arcs from "enter" to "exit", so # make sure we have at least one positive value. unpredicted = ( e for e in executed if e not in possible and e[0] != e[1] and (e[0] > 0 or e[1] > 0) ) return sorted(unpredicted) def branch_lines(self): """Returns a list of line numbers that have more than one exit.""" return [l1 for l1,count in iitems(self.exit_counts) if count > 1] def total_branches(self): """How many total branches are there?""" return sum(count for count in self.exit_counts.values() if count > 1) def missing_branch_arcs(self): """Return arcs that weren't executed from branch lines. Returns {l1:[l2a,l2b,...], ...} """ missing = self.arcs_missing() branch_lines = set(self.branch_lines()) mba = collections.defaultdict(list) for l1, l2 in missing: if l1 in branch_lines: mba[l1].append(l2) return mba def branch_stats(self): """Get stats about branches. Returns a dict mapping line numbers to a tuple: (total_exits, taken_exits). """ missing_arcs = self.missing_branch_arcs() stats = {} for lnum in self.branch_lines(): exits = self.exit_counts[lnum] try: missing = len(missing_arcs[lnum]) except KeyError: missing = 0 stats[lnum] = (exits, exits - missing) return stats class Numbers(SimpleRepr): """The numerical results of measuring coverage. This holds the basic statistics from `Analysis`, and is used to roll up statistics across files. """ # A global to determine the precision on coverage percentages, the number # of decimal places. _precision = 0 _near0 = 1.0 # These will change when _precision is changed. _near100 = 99.0 def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0, n_branches=0, n_partial_branches=0, n_missing_branches=0 ): self.n_files = n_files self.n_statements = n_statements self.n_excluded = n_excluded self.n_missing = n_missing self.n_branches = n_branches self.n_partial_branches = n_partial_branches self.n_missing_branches = n_missing_branches def init_args(self): """Return a list for __init__(*args) to recreate this object.""" return [ self.n_files, self.n_statements, self.n_excluded, self.n_missing, self.n_branches, self.n_partial_branches, self.n_missing_branches, ] @classmethod def set_precision(cls, precision): """Set the number of decimal places used to report percentages.""" assert 0 <= precision < 10 cls._precision = precision cls._near0 = 1.0 / 10**precision cls._near100 = 100.0 - cls._near0 @property def n_executed(self): """Returns the number of executed statements.""" return self.n_statements - self.n_missing @property def n_executed_branches(self): """Returns the number of executed branches.""" return self.n_branches - self.n_missing_branches @property def pc_covered(self): """Returns a single percentage value for coverage.""" if self.n_statements > 0: numerator, denominator = self.ratio_covered pc_cov = (100.0 * numerator) / denominator else: pc_cov = 100.0 return pc_cov @property def pc_covered_str(self): """Returns the percent covered, as a string, without a percent sign. Note that "0" is only returned when the value is truly zero, and "100" is only returned when the value is truly 100. Rounding can never result in either "0" or "100". """ pc = self.pc_covered if 0 < pc < self._near0: pc = self._near0 elif self._near100 < pc < 100: pc = self._near100 else: pc = round(pc, self._precision) return "%.*f" % (self._precision, pc) @classmethod def pc_str_width(cls): """How many characters wide can pc_covered_str be?""" width = 3 # "100" if cls._precision > 0: width += 1 + cls._precision return width @property def ratio_covered(self): """Return a numerator and denominator for the coverage ratio.""" numerator = self.n_executed + self.n_executed_branches denominator = self.n_statements + self.n_branches return numerator, denominator def __add__(self, other): nums = Numbers() nums.n_files = self.n_files + other.n_files nums.n_statements = self.n_statements + other.n_statements nums.n_excluded = self.n_excluded + other.n_excluded nums.n_missing = self.n_missing + other.n_missing nums.n_branches = self.n_branches + other.n_branches nums.n_partial_branches = ( self.n_partial_branches + other.n_partial_branches ) nums.n_missing_branches = ( self.n_missing_branches + other.n_missing_branches ) return nums def __radd__(self, other): # Implementing 0+Numbers allows us to sum() a list of Numbers. if other == 0: return self return NotImplemented @contract(total='number', fail_under='number', precision=int, returns=bool) def should_fail_under(total, fail_under, precision): """Determine if a total should fail due to fail-under. `total` is a float, the coverage measurement total. `fail_under` is the fail_under setting to compare with. `precision` is the number of digits to consider after the decimal point. Returns True if the total should fail. """ # Special case for fail_under=100, it must really be 100. if fail_under == 100.0 and total != 100.0: return True return round(total, precision) < fail_under python-coverage-4.5+dfsg.1.orig/coverage/debug.py0000644000076600000620000002321713173515667017734 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Control of and utilities for debugging.""" import contextlib import inspect import os import re import sys try: import _thread except ImportError: import thread as _thread from coverage.backward import StringIO from coverage.misc import isolate_module os = isolate_module(os) # When debugging, it can be helpful to force some options, especially when # debugging the configuration mechanisms you usually use to control debugging! # This is a list of forced debugging options. FORCED_DEBUG = [] # A hack for debugging testing in sub-processes. _TEST_NAME_FILE = "" # "/tmp/covtest.txt" class DebugControl(object): """Control and output for debugging.""" def __init__(self, options, output): """Configure the options and output file for debugging.""" self.options = list(options) + FORCED_DEBUG self.raw_output = output self.suppress_callers = False filters = [] if self.should('pid'): filters.append(add_pid_and_tid) self.output = DebugOutputFile( self.raw_output, show_process=self.should('process'), filters=filters, ) def __repr__(self): return "" % (self.options, self.raw_output) def should(self, option): """Decide whether to output debug information in category `option`.""" if option == "callers" and self.suppress_callers: return False return (option in self.options) @contextlib.contextmanager def without_callers(self): """A context manager to prevent call stacks from being logged.""" old = self.suppress_callers self.suppress_callers = True try: yield finally: self.suppress_callers = old def write(self, msg): """Write a line of debug output. `msg` is the line to write. A newline will be appended. """ self.output.write(msg+"\n") if self.should('callers'): dump_stack_frames(out=self.output, skip=1) self.output.flush() class DebugControlString(DebugControl): """A `DebugControl` that writes to a StringIO, for testing.""" def __init__(self, options): super(DebugControlString, self).__init__(options, StringIO()) def get_output(self): """Get the output text from the `DebugControl`.""" return self.raw_output.getvalue() def info_header(label): """Make a nice header string.""" return "--{0:-<60s}".format(" "+label+" ") def info_formatter(info): """Produce a sequence of formatted lines from info. `info` is a sequence of pairs (label, data). The produced lines are nicely formatted, ready to print. """ info = list(info) if not info: return label_len = max(len(l) for l, _d in info) for label, data in info: if data == []: data = "-none-" if isinstance(data, (list, set, tuple)): prefix = "%*s:" % (label_len, label) for e in data: yield "%*s %s" % (label_len+1, prefix, e) prefix = "" else: yield "%*s: %s" % (label_len, label, data) def write_formatted_info(writer, header, info): """Write a sequence of (label,data) pairs nicely.""" writer.write(info_header(header)) for line in info_formatter(info): writer.write(" %s" % line) def short_stack(limit=None, skip=0): """Return a string summarizing the call stack. The string is multi-line, with one line per stack frame. Each line shows the function name, the file name, and the line number: ... start_import_stop : /Users/ned/coverage/trunk/tests/coveragetest.py @95 import_local_file : /Users/ned/coverage/trunk/tests/coveragetest.py @81 import_local_file : /Users/ned/coverage/trunk/coverage/backward.py @159 ... `limit` is the number of frames to include, defaulting to all of them. `skip` is the number of frames to skip, so that debugging functions can call this and not be included in the result. """ stack = inspect.stack()[limit:skip:-1] return "\n".join("%30s : %s @%d" % (t[3], t[1], t[2]) for t in stack) def dump_stack_frames(limit=None, out=None, skip=0): """Print a summary of the stack to stdout, or someplace else.""" out = out or sys.stdout out.write(short_stack(limit=limit, skip=skip+1)) out.write("\n") def short_id(id64): """Given a 64-bit id, make a shorter 16-bit one.""" id16 = 0 for offset in range(0, 64, 16): id16 ^= id64 >> offset return id16 & 0xFFFF def add_pid_and_tid(text): """A filter to add pid and tid to debug messages.""" # Thread ids are useful, but too long. Make a shorter one. tid = "{0:04x}".format(short_id(_thread.get_ident())) text = "{0:5d}.{1}: {2}".format(os.getpid(), tid, text) return text def filter_text(text, filters): """Run `text` through a series of filters. `filters` is a list of functions. Each takes a string and returns a string. Each is run in turn. Returns: the final string that results after all of the filters have run. """ clean_text = text.rstrip() ending = text[len(clean_text):] text = clean_text for fn in filters: lines = [] for line in text.splitlines(): lines.extend(fn(line).splitlines()) text = "\n".join(lines) return text + ending class CwdTracker(object): # pragma: debugging """A class to add cwd info to debug messages.""" def __init__(self): self.cwd = None def filter(self, text): """Add a cwd message for each new cwd.""" cwd = os.getcwd() if cwd != self.cwd: text = "cwd is now {0!r}\n".format(cwd) + text self.cwd = cwd return text class DebugOutputFile(object): # pragma: debugging """A file-like object that includes pid and cwd information.""" def __init__(self, outfile, show_process, filters): self.outfile = outfile self.show_process = show_process self.filters = list(filters) if self.show_process: self.filters.append(CwdTracker().filter) cmd = " ".join(getattr(sys, 'argv', ['???'])) self.write("New process: executable: %s\n" % (sys.executable,)) self.write("New process: cmd: %s\n" % (cmd,)) if hasattr(os, 'getppid'): self.write("New process: parent pid: %s\n" % (os.getppid(),)) SYS_MOD_NAME = '$coverage.debug.DebugOutputFile.the_one' @classmethod def the_one(cls, fileobj=None, show_process=True, filters=()): """Get the process-wide singleton DebugOutputFile. If it doesn't exist yet, then create it as a wrapper around the file object `fileobj`. `show_process` controls whether the debug file adds process-level information. """ # Because of the way igor.py deletes and re-imports modules, # this class can be defined more than once. But we really want # a process-wide singleton. So stash it in sys.modules instead of # on a class attribute. Yes, this is aggressively gross. the_one = sys.modules.get(cls.SYS_MOD_NAME) if the_one is None: assert fileobj is not None sys.modules[cls.SYS_MOD_NAME] = the_one = cls(fileobj, show_process, filters) return the_one def write(self, text): """Just like file.write, but filter through all our filters.""" self.outfile.write(filter_text(text, self.filters)) self.outfile.flush() def flush(self): """Flush our file.""" self.outfile.flush() def log(msg, stack=False): # pragma: debugging """Write a log message as forcefully as possible.""" out = DebugOutputFile.the_one() out.write(msg+"\n") if stack: dump_stack_frames(out=out, skip=1) def filter_aspectlib_frames(text): # pragma: debugging """Aspectlib prints stack traces, but includes its own frames. Scrub those out.""" # <<< aspectlib/__init__.py:257:function_wrapper < igor.py:143:run_tests < ... text = re.sub(r"(?<= )aspectlib/[^.]+\.py:\d+:\w+ < ", "", text) return text def enable_aspectlib_maybe(): # pragma: debugging """For debugging, we can use aspectlib to trace execution. Define COVERAGE_ASPECTLIB to enable and configure aspectlib to trace execution:: $ export COVERAGE_LOG=covaspect.txt $ export COVERAGE_ASPECTLIB=coverage.Coverage:coverage.data.CoverageData $ coverage run blah.py ... This will trace all the public methods on Coverage and CoverageData, writing the information to covaspect.txt. """ aspects = os.environ.get("COVERAGE_ASPECTLIB", "") if not aspects: return import aspectlib # pylint: disable=import-error import aspectlib.debug # pylint: disable=import-error filename = os.environ.get("COVERAGE_LOG", "/tmp/covlog.txt") filters = [add_pid_and_tid, filter_aspectlib_frames] aspects_file = DebugOutputFile.the_one(open(filename, "a"), show_process=True, filters=filters) aspect_log = aspectlib.debug.log( print_to=aspects_file, attributes=['id'], stacktrace=30, use_logging=False ) public_methods = re.compile(r'^(__init__|[a-zA-Z].*)$') for aspect in aspects.split(':'): aspectlib.weave(aspect, aspect_log, methods=public_methods) python-coverage-4.5+dfsg.1.orig/coverage/ctracer/0000755000076600000620000000000013235414514017676 5ustar staffpython-coverage-4.5+dfsg.1.orig/coverage/ctracer/stats.h0000644000076600000620000000140413146037571021211 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ #ifndef _COVERAGE_STATS_H #define _COVERAGE_STATS_H #include "util.h" #if COLLECT_STATS #define STATS(x) x #else #define STATS(x) #endif typedef struct Stats { unsigned int calls; /* Need at least one member, but the rest only if needed. */ #if COLLECT_STATS unsigned int lines; unsigned int returns; unsigned int exceptions; unsigned int others; unsigned int files; unsigned int missed_returns; unsigned int stack_reallocs; unsigned int errors; unsigned int pycalls; unsigned int start_context_calls; #endif } Stats; #endif /* _COVERAGE_STATS_H */ python-coverage-4.5+dfsg.1.orig/coverage/ctracer/module.c0000644000076600000620000000451613146037571021342 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ #include "util.h" #include "tracer.h" #include "filedisp.h" /* Module definition */ #define MODULE_DOC PyDoc_STR("Fast coverage tracer.") #if PY_MAJOR_VERSION >= 3 static PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "coverage.tracer", MODULE_DOC, -1, NULL, /* methods */ NULL, NULL, /* traverse */ NULL, /* clear */ NULL }; PyObject * PyInit_tracer(void) { PyObject * mod = PyModule_Create(&moduledef); if (mod == NULL) { return NULL; } if (CTracer_intern_strings() < 0) { return NULL; } /* Initialize CTracer */ CTracerType.tp_new = PyType_GenericNew; if (PyType_Ready(&CTracerType) < 0) { Py_DECREF(mod); return NULL; } Py_INCREF(&CTracerType); if (PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType) < 0) { Py_DECREF(mod); Py_DECREF(&CTracerType); return NULL; } /* Initialize CFileDisposition */ CFileDispositionType.tp_new = PyType_GenericNew; if (PyType_Ready(&CFileDispositionType) < 0) { Py_DECREF(mod); Py_DECREF(&CTracerType); return NULL; } Py_INCREF(&CFileDispositionType); if (PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType) < 0) { Py_DECREF(mod); Py_DECREF(&CTracerType); Py_DECREF(&CFileDispositionType); return NULL; } return mod; } #else void inittracer(void) { PyObject * mod; mod = Py_InitModule3("coverage.tracer", NULL, MODULE_DOC); if (mod == NULL) { return; } if (CTracer_intern_strings() < 0) { return; } /* Initialize CTracer */ CTracerType.tp_new = PyType_GenericNew; if (PyType_Ready(&CTracerType) < 0) { return; } Py_INCREF(&CTracerType); PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType); /* Initialize CFileDisposition */ CFileDispositionType.tp_new = PyType_GenericNew; if (PyType_Ready(&CFileDispositionType) < 0) { return; } Py_INCREF(&CFileDispositionType); PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType); } #endif /* Py3k */ python-coverage-4.5+dfsg.1.orig/coverage/ctracer/filedisp.c0000644000076600000620000000626213146037571021654 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ #include "util.h" #include "filedisp.h" void CFileDisposition_dealloc(CFileDisposition *self) { Py_XDECREF(self->original_filename); Py_XDECREF(self->canonical_filename); Py_XDECREF(self->source_filename); Py_XDECREF(self->trace); Py_XDECREF(self->reason); Py_XDECREF(self->file_tracer); Py_XDECREF(self->has_dynamic_filename); } static PyMemberDef CFileDisposition_members[] = { { "original_filename", T_OBJECT, offsetof(CFileDisposition, original_filename), 0, PyDoc_STR("") }, { "canonical_filename", T_OBJECT, offsetof(CFileDisposition, canonical_filename), 0, PyDoc_STR("") }, { "source_filename", T_OBJECT, offsetof(CFileDisposition, source_filename), 0, PyDoc_STR("") }, { "trace", T_OBJECT, offsetof(CFileDisposition, trace), 0, PyDoc_STR("") }, { "reason", T_OBJECT, offsetof(CFileDisposition, reason), 0, PyDoc_STR("") }, { "file_tracer", T_OBJECT, offsetof(CFileDisposition, file_tracer), 0, PyDoc_STR("") }, { "has_dynamic_filename", T_OBJECT, offsetof(CFileDisposition, has_dynamic_filename), 0, PyDoc_STR("") }, { NULL } }; PyTypeObject CFileDispositionType = { MyType_HEAD_INIT "coverage.CFileDispositionType", /*tp_name*/ sizeof(CFileDisposition), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)CFileDisposition_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "CFileDisposition objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ CFileDisposition_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; python-coverage-4.5+dfsg.1.orig/coverage/ctracer/datastack.c0000644000076600000620000000262713173515667022024 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ #include "util.h" #include "datastack.h" #define STACK_DELTA 20 int DataStack_init(Stats *pstats, DataStack *pdata_stack) { pdata_stack->depth = -1; pdata_stack->stack = NULL; pdata_stack->alloc = 0; return RET_OK; } void DataStack_dealloc(Stats *pstats, DataStack *pdata_stack) { int i; for (i = 0; i < pdata_stack->alloc; i++) { Py_XDECREF(pdata_stack->stack[i].file_data); } PyMem_Free(pdata_stack->stack); } int DataStack_grow(Stats *pstats, DataStack *pdata_stack) { pdata_stack->depth++; if (pdata_stack->depth >= pdata_stack->alloc) { /* We've outgrown our data_stack array: make it bigger. */ int bigger = pdata_stack->alloc + STACK_DELTA; DataStackEntry * bigger_data_stack = PyMem_Realloc(pdata_stack->stack, bigger * sizeof(DataStackEntry)); STATS( pstats->stack_reallocs++; ) if (bigger_data_stack == NULL) { PyErr_NoMemory(); pdata_stack->depth--; return RET_ERROR; } /* Zero the new entries. */ memset(bigger_data_stack + pdata_stack->alloc, 0, STACK_DELTA * sizeof(DataStackEntry)); pdata_stack->stack = bigger_data_stack; pdata_stack->alloc = bigger; } return RET_OK; } python-coverage-4.5+dfsg.1.orig/coverage/ctracer/tracer.h0000644000076600000620000000413113173515667021342 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ #ifndef _COVERAGE_TRACER_H #define _COVERAGE_TRACER_H #include "util.h" #include "structmember.h" #include "frameobject.h" #include "opcode.h" #include "datastack.h" /* The CTracer type. */ typedef struct CTracer { PyObject_HEAD /* Python objects manipulated directly by the Collector class. */ PyObject * should_trace; PyObject * check_include; PyObject * warn; PyObject * concur_id_func; PyObject * data; PyObject * file_tracers; PyObject * should_trace_cache; PyObject * trace_arcs; PyObject * should_start_context; PyObject * switch_context; PyObject * context; /* Has the tracer been started? */ BOOL started; /* Are we tracing arcs, or just lines? */ BOOL tracing_arcs; /* Have we had any activity? */ BOOL activity; /* The data stack is a stack of dictionaries. Each dictionary collects data for a single source file. The data stack parallels the call stack: each call pushes the new frame's file data onto the data stack, and each return pops file data off. The file data is a dictionary whose form depends on the tracing options. If tracing arcs, the keys are line number pairs. If not tracing arcs, the keys are line numbers. In both cases, the value is irrelevant (None). */ DataStack data_stack; /* Used if we aren't doing concurrency. */ PyObject * data_stack_index; /* Used if we are doing concurrency. */ DataStack * data_stacks; int data_stacks_alloc; int data_stacks_used; DataStack * pdata_stack; /* The current file's data stack entry. */ DataStackEntry * pcur_entry; /* The parent frame for the last exception event, to fix missing returns. */ PyFrameObject * last_exc_back; int last_exc_firstlineno; Stats stats; } CTracer; int CTracer_intern_strings(void); extern PyTypeObject CTracerType; #endif /* _COVERAGE_TRACER_H */ python-coverage-4.5+dfsg.1.orig/coverage/ctracer/util.h0000644000076600000620000000501013173515667021034 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ #ifndef _COVERAGE_UTIL_H #define _COVERAGE_UTIL_H #include /* Compile-time debugging helpers */ #undef WHAT_LOG /* Define to log the WHAT params in the trace function. */ #undef TRACE_LOG /* Define to log our bookkeeping. */ #undef COLLECT_STATS /* Collect counters: stats are printed when tracer is stopped. */ #undef DO_NOTHING /* Define this to make the tracer do nothing. */ /* Py 2.x and 3.x compatibility */ #if PY_MAJOR_VERSION >= 3 #define MyText_Type PyUnicode_Type #define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o) #define MyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o) #define MyBytes_AS_STRING(o) PyBytes_AS_STRING(o) #define MyText_AsString(o) PyUnicode_AsUTF8(o) #define MyText_FromFormat PyUnicode_FromFormat #define MyInt_FromInt(i) PyLong_FromLong((long)i) #define MyInt_AsInt(o) (int)PyLong_AsLong(o) #define MyText_InternFromString(s) PyUnicode_InternFromString(s) #define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0) #else #define MyText_Type PyString_Type #define MyText_AS_BYTES(o) (Py_INCREF(o), o) #define MyBytes_GET_SIZE(o) PyString_GET_SIZE(o) #define MyBytes_AS_STRING(o) PyString_AS_STRING(o) #define MyText_AsString(o) PyString_AsString(o) #define MyText_FromFormat PyUnicode_FromFormat #define MyInt_FromInt(i) PyInt_FromLong((long)i) #define MyInt_AsInt(o) (int)PyInt_AsLong(o) #define MyText_InternFromString(s) PyString_InternFromString(s) #define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0, #endif /* Py3k */ // Undocumented, and not in 2.6, so our own copy of it. #define My_XSETREF(op, op2) \ do { \ PyObject *_py_tmp = (PyObject *)(op); \ (op) = (op2); \ Py_XDECREF(_py_tmp); \ } while (0) /* The values returned to indicate ok or error. */ #define RET_OK 0 #define RET_ERROR -1 /* Nicer booleans */ typedef int BOOL; #define FALSE 0 #define TRUE 1 /* Only for extreme machete-mode debugging! */ #define CRASH { printf("*** CRASH! ***\n"); *((int*)1) = 1; } #endif /* _COVERAGE_UTIL_H */ python-coverage-4.5+dfsg.1.orig/coverage/ctracer/datastack.h0000644000076600000620000000301413173515667022020 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ #ifndef _COVERAGE_DATASTACK_H #define _COVERAGE_DATASTACK_H #include "util.h" #include "stats.h" /* An entry on the data stack. For each call frame, we need to record all * the information needed for CTracer_handle_line to operate as quickly as * possible. */ typedef struct DataStackEntry { /* The current file_data dictionary. Owned. */ PyObject * file_data; /* The disposition object for this frame. A borrowed instance of CFileDisposition. */ PyObject * disposition; /* The FileTracer handling this frame, or None if it's Python. Borrowed. */ PyObject * file_tracer; /* The line number of the last line recorded, for tracing arcs. -1 means there was no previous line, as when entering a code object. */ int last_line; BOOL started_context; } DataStackEntry; /* A data stack is a dynamically allocated vector of DataStackEntry's. */ typedef struct DataStack { int depth; /* The index of the last-used entry in stack. */ int alloc; /* number of entries allocated at stack. */ /* The file data at each level, or NULL if not recording. */ DataStackEntry * stack; } DataStack; int DataStack_init(Stats * pstats, DataStack *pdata_stack); void DataStack_dealloc(Stats * pstats, DataStack *pdata_stack); int DataStack_grow(Stats * pstats, DataStack *pdata_stack); #endif /* _COVERAGE_DATASTACK_H */ python-coverage-4.5+dfsg.1.orig/coverage/ctracer/tracer.c0000644000076600000620000010777013224235252021334 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ /* C-based Tracer for coverage.py. */ #include "util.h" #include "datastack.h" #include "filedisp.h" #include "tracer.h" /* Python C API helpers. */ static int pyint_as_int(PyObject * pyint, int *pint) { int the_int = MyInt_AsInt(pyint); if (the_int == -1 && PyErr_Occurred()) { return RET_ERROR; } *pint = the_int; return RET_OK; } /* Interned strings to speed GetAttr etc. */ static PyObject *str_trace; static PyObject *str_file_tracer; static PyObject *str__coverage_enabled; static PyObject *str__coverage_plugin; static PyObject *str__coverage_plugin_name; static PyObject *str_dynamic_source_filename; static PyObject *str_line_number_range; int CTracer_intern_strings(void) { int ret = RET_ERROR; #define INTERN_STRING(v, s) \ v = MyText_InternFromString(s); \ if (v == NULL) { \ goto error; \ } INTERN_STRING(str_trace, "trace") INTERN_STRING(str_file_tracer, "file_tracer") INTERN_STRING(str__coverage_enabled, "_coverage_enabled") INTERN_STRING(str__coverage_plugin, "_coverage_plugin") INTERN_STRING(str__coverage_plugin_name, "_coverage_plugin_name") INTERN_STRING(str_dynamic_source_filename, "dynamic_source_filename") INTERN_STRING(str_line_number_range, "line_number_range") ret = RET_OK; error: return ret; } static void CTracer_disable_plugin(CTracer *self, PyObject * disposition); static int CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused) { int ret = RET_ERROR; if (DataStack_init(&self->stats, &self->data_stack) < 0) { goto error; } self->pdata_stack = &self->data_stack; self->context = Py_None; Py_INCREF(self->context); ret = RET_OK; goto ok; error: STATS( self->stats.errors++; ) ok: return ret; } static void CTracer_dealloc(CTracer *self) { int i; if (self->started) { PyEval_SetTrace(NULL, NULL); } Py_XDECREF(self->should_trace); Py_XDECREF(self->check_include); Py_XDECREF(self->warn); Py_XDECREF(self->concur_id_func); Py_XDECREF(self->data); Py_XDECREF(self->file_tracers); Py_XDECREF(self->should_trace_cache); Py_XDECREF(self->should_start_context); Py_XDECREF(self->switch_context); Py_XDECREF(self->context); DataStack_dealloc(&self->stats, &self->data_stack); if (self->data_stacks) { for (i = 0; i < self->data_stacks_used; i++) { DataStack_dealloc(&self->stats, self->data_stacks + i); } PyMem_Free(self->data_stacks); } Py_XDECREF(self->data_stack_index); Py_TYPE(self)->tp_free((PyObject*)self); } #if TRACE_LOG static const char * indent(int n) { static const char * spaces = " " " " " " " " ; return spaces + strlen(spaces) - n*2; } static BOOL logging = FALSE; /* Set these constants to be a file substring and line number to start logging. */ static const char * start_file = "tests/views"; static int start_line = 27; static void showlog(int depth, int lineno, PyObject * filename, const char * msg) { if (logging) { printf("%s%3d ", indent(depth), depth); if (lineno) { printf("%4d", lineno); } else { printf(" "); } if (filename) { PyObject *ascii = MyText_AS_BYTES(filename); printf(" %s", MyBytes_AS_STRING(ascii)); Py_DECREF(ascii); } if (msg) { printf(" %s", msg); } printf("\n"); } } #define SHOWLOG(a,b,c,d) showlog(a,b,c,d) #else #define SHOWLOG(a,b,c,d) #endif /* TRACE_LOG */ #if WHAT_LOG static const char * what_sym[] = {"CALL", "EXC ", "LINE", "RET "}; #endif /* Record a pair of integers in self->pcur_entry->file_data. */ static int CTracer_record_pair(CTracer *self, int l1, int l2) { int ret = RET_ERROR; PyObject * t = NULL; t = Py_BuildValue("(ii)", l1, l2); if (t == NULL) { goto error; } if (PyDict_SetItem(self->pcur_entry->file_data, t, Py_None) < 0) { goto error; } ret = RET_OK; error: Py_XDECREF(t); return ret; } /* Set self->pdata_stack to the proper data_stack to use. */ static int CTracer_set_pdata_stack(CTracer *self) { int ret = RET_ERROR; PyObject * co_obj = NULL; PyObject * stack_index = NULL; if (self->concur_id_func != Py_None) { int the_index = 0; if (self->data_stack_index == NULL) { PyObject * weakref = NULL; weakref = PyImport_ImportModule("weakref"); if (weakref == NULL) { goto error; } STATS( self->stats.pycalls++; ) self->data_stack_index = PyObject_CallMethod(weakref, "WeakKeyDictionary", NULL); Py_XDECREF(weakref); if (self->data_stack_index == NULL) { goto error; } } STATS( self->stats.pycalls++; ) co_obj = PyObject_CallObject(self->concur_id_func, NULL); if (co_obj == NULL) { goto error; } stack_index = PyObject_GetItem(self->data_stack_index, co_obj); if (stack_index == NULL) { /* PyObject_GetItem sets an exception if it didn't find the thing. */ PyErr_Clear(); /* A new concurrency object. Make a new data stack. */ the_index = self->data_stacks_used; stack_index = MyInt_FromInt(the_index); if (stack_index == NULL) { goto error; } if (PyObject_SetItem(self->data_stack_index, co_obj, stack_index) < 0) { goto error; } self->data_stacks_used++; if (self->data_stacks_used >= self->data_stacks_alloc) { int bigger = self->data_stacks_alloc + 10; DataStack * bigger_stacks = PyMem_Realloc(self->data_stacks, bigger * sizeof(DataStack)); if (bigger_stacks == NULL) { PyErr_NoMemory(); goto error; } self->data_stacks = bigger_stacks; self->data_stacks_alloc = bigger; } DataStack_init(&self->stats, &self->data_stacks[the_index]); } else { if (pyint_as_int(stack_index, &the_index) < 0) { goto error; } } self->pdata_stack = &self->data_stacks[the_index]; } else { self->pdata_stack = &self->data_stack; } ret = RET_OK; error: Py_XDECREF(co_obj); Py_XDECREF(stack_index); return ret; } /* * Parts of the trace function. */ static int CTracer_check_missing_return(CTracer *self, PyFrameObject *frame) { int ret = RET_ERROR; if (self->last_exc_back) { if (frame == self->last_exc_back) { /* Looks like someone forgot to send a return event. We'll clear the exception state and do the RETURN code here. Notice that the frame we have in hand here is not the correct frame for the RETURN, that frame is gone. Our handling for RETURN doesn't need the actual frame, but we do log it, so that will look a little off if you're looking at the detailed log. If someday we need to examine the frame when doing RETURN, then we'll need to keep more of the missed frame's state. */ STATS( self->stats.missed_returns++; ) if (CTracer_set_pdata_stack(self) < 0) { goto error; } if (self->pdata_stack->depth >= 0) { if (self->tracing_arcs && self->pcur_entry->file_data) { if (CTracer_record_pair(self, self->pcur_entry->last_line, -self->last_exc_firstlineno) < 0) { goto error; } } SHOWLOG(self->pdata_stack->depth, frame->f_lineno, frame->f_code->co_filename, "missedreturn"); self->pdata_stack->depth--; self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth]; } } self->last_exc_back = NULL; } ret = RET_OK; error: return ret; } static int CTracer_handle_call(CTracer *self, PyFrameObject *frame) { int ret = RET_ERROR; int ret2; /* Owned references that we clean up at the very end of the function. */ PyObject * disposition = NULL; PyObject * plugin = NULL; PyObject * plugin_name = NULL; PyObject * next_tracename = NULL; /* Borrowed references. */ PyObject * filename = NULL; PyObject * disp_trace = NULL; PyObject * tracename = NULL; PyObject * file_tracer = NULL; PyObject * has_dynamic_filename = NULL; CFileDisposition * pdisp = NULL; STATS( self->stats.calls++; ) self->activity = TRUE; /* Grow the stack. */ if (CTracer_set_pdata_stack(self) < 0) { goto error; } if (DataStack_grow(&self->stats, self->pdata_stack) < 0) { goto error; } self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth]; /* See if this frame begins a new context. */ if (self->should_start_context && self->context == Py_None) { PyObject * context; /* We're looking for our context, ask should_start_context if this is the start. */ STATS( self->stats.start_context_calls++; ) STATS( self->stats.pycalls++; ) context = PyObject_CallFunctionObjArgs(self->should_start_context, frame, NULL); if (context == NULL) { goto error; } if (context != Py_None) { PyObject * val; Py_DECREF(self->context); self->context = context; self->pcur_entry->started_context = TRUE; STATS( self->stats.pycalls++; ) val = PyObject_CallFunctionObjArgs(self->switch_context, context, NULL); if (val == NULL) { goto error; } Py_DECREF(val); } else { Py_DECREF(context); self->pcur_entry->started_context = FALSE; } } else { self->pcur_entry->started_context = FALSE; } /* Check if we should trace this line. */ filename = frame->f_code->co_filename; disposition = PyDict_GetItem(self->should_trace_cache, filename); if (disposition == NULL) { if (PyErr_Occurred()) { goto error; } STATS( self->stats.files++; ) /* We've never considered this file before. */ /* Ask should_trace about it. */ STATS( self->stats.pycalls++; ) disposition = PyObject_CallFunctionObjArgs(self->should_trace, filename, frame, NULL); if (disposition == NULL) { /* An error occurred inside should_trace. */ goto error; } if (PyDict_SetItem(self->should_trace_cache, filename, disposition) < 0) { goto error; } } else { Py_INCREF(disposition); } if (disposition == Py_None) { /* A later check_include returned false, so don't trace it. */ disp_trace = Py_False; } else { /* The object we got is a CFileDisposition, use it efficiently. */ pdisp = (CFileDisposition *) disposition; disp_trace = pdisp->trace; if (disp_trace == NULL) { goto error; } } if (disp_trace == Py_True) { /* If tracename is a string, then we're supposed to trace. */ tracename = pdisp->source_filename; if (tracename == NULL) { goto error; } file_tracer = pdisp->file_tracer; if (file_tracer == NULL) { goto error; } if (file_tracer != Py_None) { plugin = PyObject_GetAttr(file_tracer, str__coverage_plugin); if (plugin == NULL) { goto error; } plugin_name = PyObject_GetAttr(plugin, str__coverage_plugin_name); if (plugin_name == NULL) { goto error; } } has_dynamic_filename = pdisp->has_dynamic_filename; if (has_dynamic_filename == NULL) { goto error; } if (has_dynamic_filename == Py_True) { STATS( self->stats.pycalls++; ) next_tracename = PyObject_CallMethodObjArgs( file_tracer, str_dynamic_source_filename, tracename, frame, NULL ); if (next_tracename == NULL) { /* An exception from the function. Alert the user with a * warning and a traceback. */ CTracer_disable_plugin(self, disposition); /* Because we handled the error, goto ok. */ goto ok; } tracename = next_tracename; if (tracename != Py_None) { /* Check the dynamic source filename against the include rules. */ PyObject * included = NULL; int should_include; included = PyDict_GetItem(self->should_trace_cache, tracename); if (included == NULL) { PyObject * should_include_bool; if (PyErr_Occurred()) { goto error; } STATS( self->stats.files++; ) STATS( self->stats.pycalls++; ) should_include_bool = PyObject_CallFunctionObjArgs(self->check_include, tracename, frame, NULL); if (should_include_bool == NULL) { goto error; } should_include = (should_include_bool == Py_True); Py_DECREF(should_include_bool); if (PyDict_SetItem(self->should_trace_cache, tracename, should_include ? disposition : Py_None) < 0) { goto error; } } else { should_include = (included != Py_None); } if (!should_include) { tracename = Py_None; } } } } else { tracename = Py_None; } if (tracename != Py_None) { PyObject * file_data = PyDict_GetItem(self->data, tracename); if (file_data == NULL) { if (PyErr_Occurred()) { goto error; } file_data = PyDict_New(); if (file_data == NULL) { goto error; } ret2 = PyDict_SetItem(self->data, tracename, file_data); if (ret2 < 0) { goto error; } /* If the disposition mentions a plugin, record that. */ if (file_tracer != Py_None) { ret2 = PyDict_SetItem(self->file_tracers, tracename, plugin_name); if (ret2 < 0) { goto error; } } } else { /* PyDict_GetItem gives a borrowed reference. Own it. */ Py_INCREF(file_data); } Py_XDECREF(self->pcur_entry->file_data); self->pcur_entry->file_data = file_data; self->pcur_entry->file_tracer = file_tracer; SHOWLOG(self->pdata_stack->depth, frame->f_lineno, filename, "traced"); } else { Py_XDECREF(self->pcur_entry->file_data); self->pcur_entry->file_data = NULL; self->pcur_entry->file_tracer = Py_None; SHOWLOG(self->pdata_stack->depth, frame->f_lineno, filename, "skipped"); } self->pcur_entry->disposition = disposition; /* Make the frame right in case settrace(gettrace()) happens. */ Py_INCREF(self); My_XSETREF(frame->f_trace, (PyObject*)self); /* A call event is really a "start frame" event, and can happen for * re-entering a generator also. f_lasti is -1 for a true call, and a * real byte offset for a generator re-entry. */ if (frame->f_lasti < 0) { self->pcur_entry->last_line = -frame->f_code->co_firstlineno; } else { self->pcur_entry->last_line = frame->f_lineno; } ok: ret = RET_OK; error: Py_XDECREF(next_tracename); Py_XDECREF(disposition); Py_XDECREF(plugin); Py_XDECREF(plugin_name); return ret; } static void CTracer_disable_plugin(CTracer *self, PyObject * disposition) { PyObject * file_tracer = NULL; PyObject * plugin = NULL; PyObject * plugin_name = NULL; PyObject * msg = NULL; PyObject * ignored = NULL; PyErr_Print(); file_tracer = PyObject_GetAttr(disposition, str_file_tracer); if (file_tracer == NULL) { goto error; } if (file_tracer == Py_None) { /* This shouldn't happen... */ goto ok; } plugin = PyObject_GetAttr(file_tracer, str__coverage_plugin); if (plugin == NULL) { goto error; } plugin_name = PyObject_GetAttr(plugin, str__coverage_plugin_name); if (plugin_name == NULL) { goto error; } msg = MyText_FromFormat( "Disabling plug-in '%s' due to previous exception", MyText_AsString(plugin_name) ); if (msg == NULL) { goto error; } STATS( self->stats.pycalls++; ) ignored = PyObject_CallFunctionObjArgs(self->warn, msg, NULL); if (ignored == NULL) { goto error; } /* Disable the plugin for future files, and stop tracing this file. */ if (PyObject_SetAttr(plugin, str__coverage_enabled, Py_False) < 0) { goto error; } if (PyObject_SetAttr(disposition, str_trace, Py_False) < 0) { goto error; } goto ok; error: /* This function doesn't return a status, so if an error happens, print it, * but don't interrupt the flow. */ /* PySys_WriteStderr is nicer, but is not in the public API. */ fprintf(stderr, "Error occurred while disabling plug-in:\n"); PyErr_Print(); ok: Py_XDECREF(file_tracer); Py_XDECREF(plugin); Py_XDECREF(plugin_name); Py_XDECREF(msg); Py_XDECREF(ignored); } static int CTracer_unpack_pair(CTracer *self, PyObject *pair, int *p_one, int *p_two) { int ret = RET_ERROR; int the_int; PyObject * pyint = NULL; int index; if (!PyTuple_Check(pair) || PyTuple_Size(pair) != 2) { PyErr_SetString( PyExc_TypeError, "line_number_range must return 2-tuple" ); goto error; } for (index = 0; index < 2; index++) { pyint = PyTuple_GetItem(pair, index); if (pyint == NULL) { goto error; } if (pyint_as_int(pyint, &the_int) < 0) { goto error; } *(index == 0 ? p_one : p_two) = the_int; } ret = RET_OK; error: return ret; } static int CTracer_handle_line(CTracer *self, PyFrameObject *frame) { int ret = RET_ERROR; int ret2; STATS( self->stats.lines++; ) if (self->pdata_stack->depth >= 0) { SHOWLOG(self->pdata_stack->depth, frame->f_lineno, frame->f_code->co_filename, "line"); if (self->pcur_entry->file_data) { int lineno_from = -1; int lineno_to = -1; /* We're tracing in this frame: record something. */ if (self->pcur_entry->file_tracer != Py_None) { PyObject * from_to = NULL; STATS( self->stats.pycalls++; ) from_to = PyObject_CallMethodObjArgs(self->pcur_entry->file_tracer, str_line_number_range, frame, NULL); if (from_to == NULL) { goto error; } ret2 = CTracer_unpack_pair(self, from_to, &lineno_from, &lineno_to); Py_DECREF(from_to); if (ret2 < 0) { CTracer_disable_plugin(self, self->pcur_entry->disposition); goto ok; } } else { lineno_from = lineno_to = frame->f_lineno; } if (lineno_from != -1) { for (; lineno_from <= lineno_to; lineno_from++) { if (self->tracing_arcs) { /* Tracing arcs: key is (last_line,this_line). */ if (CTracer_record_pair(self, self->pcur_entry->last_line, lineno_from) < 0) { goto error; } } else { /* Tracing lines: key is simply this_line. */ PyObject * this_line = MyInt_FromInt(lineno_from); if (this_line == NULL) { goto error; } ret2 = PyDict_SetItem(self->pcur_entry->file_data, this_line, Py_None); Py_DECREF(this_line); if (ret2 < 0) { goto error; } } self->pcur_entry->last_line = lineno_from; } } } } ok: ret = RET_OK; error: return ret; } static int CTracer_handle_return(CTracer *self, PyFrameObject *frame) { int ret = RET_ERROR; STATS( self->stats.returns++; ) /* A near-copy of this code is above in the missing-return handler. */ if (CTracer_set_pdata_stack(self) < 0) { goto error; } self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth]; if (self->pdata_stack->depth >= 0) { if (self->tracing_arcs && self->pcur_entry->file_data) { /* Need to distinguish between RETURN_VALUE and YIELD_VALUE. Read * the current bytecode to see what it is. In unusual circumstances * (Cython code), co_code can be the empty string, so range-check * f_lasti before reading the byte. */ int bytecode = RETURN_VALUE; PyObject * pCode = frame->f_code->co_code; int lasti = frame->f_lasti; if (lasti < MyBytes_GET_SIZE(pCode)) { bytecode = MyBytes_AS_STRING(pCode)[lasti]; } if (bytecode != YIELD_VALUE) { int first = frame->f_code->co_firstlineno; if (CTracer_record_pair(self, self->pcur_entry->last_line, -first) < 0) { goto error; } } } /* If this frame started a context, then returning from it ends the context. */ if (self->pcur_entry->started_context) { PyObject * val; Py_DECREF(self->context); self->context = Py_None; Py_INCREF(self->context); STATS( self->stats.pycalls++; ) val = PyObject_CallFunctionObjArgs(self->switch_context, self->context, NULL); if (val == NULL) { goto error; } Py_DECREF(val); } /* Pop the stack. */ SHOWLOG(self->pdata_stack->depth, frame->f_lineno, frame->f_code->co_filename, "return"); self->pdata_stack->depth--; self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth]; } ret = RET_OK; error: return ret; } static int CTracer_handle_exception(CTracer *self, PyFrameObject *frame) { /* Some code (Python 2.3, and pyexpat anywhere) fires an exception event without a return event. To detect that, we'll keep a copy of the parent frame for an exception event. If the next event is in that frame, then we must have returned without a return event. We can synthesize the missing event then. Python itself fixed this problem in 2.4. Pyexpat still has the bug. I've reported the problem with pyexpat as http://bugs.python.org/issue6359 . If it gets fixed, this code should still work properly. Maybe some day the bug will be fixed everywhere coverage.py is supported, and we can remove this missing-return detection. More about this fix: https://nedbatchelder.com/blog/200907/a_nasty_little_bug.html */ STATS( self->stats.exceptions++; ) self->last_exc_back = frame->f_back; self->last_exc_firstlineno = frame->f_code->co_firstlineno; return RET_OK; } /* * The Trace Function */ static int CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) { int ret = RET_ERROR; #if DO_NOTHING return RET_OK; #endif if (!self->started) { /* If CTracer.stop() has been called from another thread, the tracer is still active in the current thread. Let's deactivate ourselves now. */ PyEval_SetTrace(NULL, NULL); return RET_OK; } #if WHAT_LOG || TRACE_LOG PyObject * ascii = NULL; #endif #if WHAT_LOG if (what <= (int)(sizeof(what_sym)/sizeof(const char *))) { ascii = MyText_AS_BYTES(frame->f_code->co_filename); printf("trace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), frame->f_lineno); Py_DECREF(ascii); } #endif #if TRACE_LOG ascii = MyText_AS_BYTES(frame->f_code->co_filename); if (strstr(MyBytes_AS_STRING(ascii), start_file) && frame->f_lineno == start_line) { logging = TRUE; } Py_DECREF(ascii); #endif /* See below for details on missing-return detection. */ if (CTracer_check_missing_return(self, frame) < 0) { goto error; } switch (what) { case PyTrace_CALL: if (CTracer_handle_call(self, frame) < 0) { goto error; } break; case PyTrace_RETURN: if (CTracer_handle_return(self, frame) < 0) { goto error; } break; case PyTrace_LINE: if (CTracer_handle_line(self, frame) < 0) { goto error; } break; case PyTrace_EXCEPTION: if (CTracer_handle_exception(self, frame) < 0) { goto error; } break; default: STATS( self->stats.others++; ) break; } ret = RET_OK; goto cleanup; error: STATS( self->stats.errors++; ) cleanup: return ret; } /* * Python has two ways to set the trace function: sys.settrace(fn), which * takes a Python callable, and PyEval_SetTrace(func, obj), which takes * a C function and a Python object. The way these work together is that * sys.settrace(pyfn) calls PyEval_SetTrace(builtin_func, pyfn), using the * Python callable as the object in PyEval_SetTrace. So sys.gettrace() * simply returns the Python object used as the second argument to * PyEval_SetTrace. So sys.gettrace() will return our self parameter, which * means it must be callable to be used in sys.settrace(). * * So we make ourself callable, equivalent to invoking our trace function. * * To help with the process of replaying stored frames, this function has an * optional keyword argument: * * def CTracer_call(frame, event, arg, lineno=0) * * If provided, the lineno argument is used as the line number, and the * frame's f_lineno member is ignored. */ static PyObject * CTracer_call(CTracer *self, PyObject *args, PyObject *kwds) { PyFrameObject *frame; PyObject *what_str; PyObject *arg; int lineno = 0; int what; int orig_lineno; PyObject *ret = NULL; PyObject * ascii = NULL; #if DO_NOTHING CRASH #endif static char *what_names[] = { "call", "exception", "line", "return", "c_call", "c_exception", "c_return", NULL }; static char *kwlist[] = {"frame", "event", "arg", "lineno", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O|i:Tracer_call", kwlist, &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg, &lineno)) { goto done; } /* In Python, the what argument is a string, we need to find an int for the C function. */ for (what = 0; what_names[what]; what++) { int should_break; ascii = MyText_AS_BYTES(what_str); should_break = !strcmp(MyBytes_AS_STRING(ascii), what_names[what]); Py_DECREF(ascii); if (should_break) { break; } } #if WHAT_LOG ascii = MyText_AS_BYTES(frame->f_code->co_filename); printf("pytrace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), frame->f_lineno); Py_DECREF(ascii); #endif /* Save off the frame's lineno, and use the forced one, if provided. */ orig_lineno = frame->f_lineno; if (lineno > 0) { frame->f_lineno = lineno; } /* Invoke the C function, and return ourselves. */ if (CTracer_trace(self, frame, what, arg) == RET_OK) { Py_INCREF(self); ret = (PyObject *)self; } /* Clean up. */ frame->f_lineno = orig_lineno; /* For better speed, install ourselves the C way so that future calls go directly to CTracer_trace, without this intermediate function. Only do this if this is a CALL event, since new trace functions only take effect then. If we don't condition it on CALL, then we'll clobber the new trace function before it has a chance to get called. To understand why, there are three internal values to track: frame.f_trace, c_tracefunc, and c_traceobj. They are explained here: https://nedbatchelder.com/text/trace-function.html Without the conditional on PyTrace_CALL, this is what happens: def func(): # f_trace c_tracefunc c_traceobj # -------------- -------------- -------------- # CTracer CTracer.trace CTracer sys.settrace(my_func) # CTracer trampoline my_func # Now Python calls trampoline(CTracer), which calls this function # which calls PyEval_SetTrace below, setting us as the tracer again: # CTracer CTracer.trace CTracer # and it's as if the settrace never happened. */ if (what == PyTrace_CALL) { PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self); } done: return ret; } static PyObject * CTracer_start(CTracer *self, PyObject *args_unused) { PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self); self->started = TRUE; self->tracing_arcs = self->trace_arcs && PyObject_IsTrue(self->trace_arcs); /* start() returns a trace function usable with sys.settrace() */ Py_INCREF(self); return (PyObject *)self; } static PyObject * CTracer_stop(CTracer *self, PyObject *args_unused) { if (self->started) { /* Set the started flag only. The actual call to PyEval_SetTrace(NULL, NULL) is delegated to the callback itself to ensure that it called from the right thread. */ self->started = FALSE; } Py_RETURN_NONE; } static PyObject * CTracer_activity(CTracer *self, PyObject *args_unused) { if (self->activity) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } static PyObject * CTracer_reset_activity(CTracer *self, PyObject *args_unused) { self->activity = FALSE; Py_RETURN_NONE; } static PyObject * CTracer_get_stats(CTracer *self, PyObject *args_unused) { #if COLLECT_STATS return Py_BuildValue( "{sI,sI,sI,sI,sI,sI,sI,sI,si,sI,sI,sI}", "calls", self->stats.calls, "lines", self->stats.lines, "returns", self->stats.returns, "exceptions", self->stats.exceptions, "others", self->stats.others, "files", self->stats.files, "missed_returns", self->stats.missed_returns, "stack_reallocs", self->stats.stack_reallocs, "stack_alloc", self->pdata_stack->alloc, "errors", self->stats.errors, "pycalls", self->stats.pycalls, "start_context_calls", self->stats.start_context_calls ); #else Py_RETURN_NONE; #endif /* COLLECT_STATS */ } static PyMemberDef CTracer_members[] = { { "should_trace", T_OBJECT, offsetof(CTracer, should_trace), 0, PyDoc_STR("Function indicating whether to trace a file.") }, { "check_include", T_OBJECT, offsetof(CTracer, check_include), 0, PyDoc_STR("Function indicating whether to include a file.") }, { "warn", T_OBJECT, offsetof(CTracer, warn), 0, PyDoc_STR("Function for issuing warnings.") }, { "concur_id_func", T_OBJECT, offsetof(CTracer, concur_id_func), 0, PyDoc_STR("Function for determining concurrency context") }, { "data", T_OBJECT, offsetof(CTracer, data), 0, PyDoc_STR("The raw dictionary of trace data.") }, { "file_tracers", T_OBJECT, offsetof(CTracer, file_tracers), 0, PyDoc_STR("Mapping from file name to plugin name.") }, { "should_trace_cache", T_OBJECT, offsetof(CTracer, should_trace_cache), 0, PyDoc_STR("Dictionary caching should_trace results.") }, { "trace_arcs", T_OBJECT, offsetof(CTracer, trace_arcs), 0, PyDoc_STR("Should we trace arcs, or just lines?") }, { "should_start_context", T_OBJECT, offsetof(CTracer, should_start_context), 0, PyDoc_STR("Function for starting contexts.") }, { "switch_context", T_OBJECT, offsetof(CTracer, switch_context), 0, PyDoc_STR("Function for switching to a new context.") }, { NULL } }; static PyMethodDef CTracer_methods[] = { { "start", (PyCFunction) CTracer_start, METH_VARARGS, PyDoc_STR("Start the tracer") }, { "stop", (PyCFunction) CTracer_stop, METH_VARARGS, PyDoc_STR("Stop the tracer") }, { "get_stats", (PyCFunction) CTracer_get_stats, METH_VARARGS, PyDoc_STR("Get statistics about the tracing") }, { "activity", (PyCFunction) CTracer_activity, METH_VARARGS, PyDoc_STR("Has there been any activity?") }, { "reset_activity", (PyCFunction) CTracer_reset_activity, METH_VARARGS, PyDoc_STR("Reset the activity flag") }, { NULL } }; PyTypeObject CTracerType = { MyType_HEAD_INIT "coverage.CTracer", /*tp_name*/ sizeof(CTracer), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)CTracer_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ (ternaryfunc)CTracer_call, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "CTracer objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ CTracer_methods, /* tp_methods */ CTracer_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)CTracer_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; python-coverage-4.5+dfsg.1.orig/coverage/ctracer/filedisp.h0000644000076600000620000000125613146037571021657 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ #ifndef _COVERAGE_FILEDISP_H #define _COVERAGE_FILEDISP_H #include "util.h" #include "structmember.h" typedef struct CFileDisposition { PyObject_HEAD PyObject * original_filename; PyObject * canonical_filename; PyObject * source_filename; PyObject * trace; PyObject * reason; PyObject * file_tracer; PyObject * has_dynamic_filename; } CFileDisposition; void CFileDisposition_dealloc(CFileDisposition *self); extern PyTypeObject CFileDispositionType; #endif /* _COVERAGE_FILEDISP_H */ python-coverage-4.5+dfsg.1.orig/coverage/phystokens.py0000644000076600000620000002356313146037571021052 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Better tokenizing for coverage.py.""" import codecs import keyword import re import sys import token import tokenize from coverage import env from coverage.backward import iternext, unicode_class from coverage.misc import contract def phys_tokens(toks): """Return all physical tokens, even line continuations. tokenize.generate_tokens() doesn't return a token for the backslash that continues lines. This wrapper provides those tokens so that we can re-create a faithful representation of the original source. Returns the same values as generate_tokens() """ last_line = None last_lineno = -1 last_ttype = None for ttype, ttext, (slineno, scol), (elineno, ecol), ltext in toks: if last_lineno != elineno: if last_line and last_line.endswith("\\\n"): # We are at the beginning of a new line, and the last line # ended with a backslash. We probably have to inject a # backslash token into the stream. Unfortunately, there's more # to figure out. This code:: # # usage = """\ # HEY THERE # """ # # triggers this condition, but the token text is:: # # '"""\\\nHEY THERE\n"""' # # so we need to figure out if the backslash is already in the # string token or not. inject_backslash = True if last_ttype == tokenize.COMMENT: # Comments like this \ # should never result in a new token. inject_backslash = False elif ttype == token.STRING: if "\n" in ttext and ttext.split('\n', 1)[0][-1] == '\\': # It's a multi-line string and the first line ends with # a backslash, so we don't need to inject another. inject_backslash = False if inject_backslash: # Figure out what column the backslash is in. ccol = len(last_line.split("\n")[-2]) - 1 # Yield the token, with a fake token type. yield ( 99999, "\\\n", (slineno, ccol), (slineno, ccol+2), last_line ) last_line = ltext last_ttype = ttype yield ttype, ttext, (slineno, scol), (elineno, ecol), ltext last_lineno = elineno @contract(source='unicode') def source_token_lines(source): """Generate a series of lines, one for each line in `source`. Each line is a list of pairs, each pair is a token:: [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ] Each pair has a token class, and the token text. If you concatenate all the token texts, and then join them with newlines, you should have your original `source` back, with two differences: trailing whitespace is not preserved, and a final line with no newline is indistinguishable from a final line with a newline. """ ws_tokens = set([token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL]) line = [] col = 0 source = source.expandtabs(8).replace('\r\n', '\n') tokgen = generate_tokens(source) for ttype, ttext, (_, scol), (_, ecol), _ in phys_tokens(tokgen): mark_start = True for part in re.split('(\n)', ttext): if part == '\n': yield line line = [] col = 0 mark_end = False elif part == '': mark_end = False elif ttype in ws_tokens: mark_end = False else: if mark_start and scol > col: line.append(("ws", u" " * (scol - col))) mark_start = False tok_class = tokenize.tok_name.get(ttype, 'xx').lower()[:3] if ttype == token.NAME and keyword.iskeyword(ttext): tok_class = "key" line.append((tok_class, part)) mark_end = True scol = 0 if mark_end: col = ecol if line: yield line class CachedTokenizer(object): """A one-element cache around tokenize.generate_tokens. When reporting, coverage.py tokenizes files twice, once to find the structure of the file, and once to syntax-color it. Tokenizing is expensive, and easily cached. This is a one-element cache so that our twice-in-a-row tokenizing doesn't actually tokenize twice. """ def __init__(self): self.last_text = None self.last_tokens = None @contract(text='unicode') def generate_tokens(self, text): """A stand-in for `tokenize.generate_tokens`.""" if text != self.last_text: self.last_text = text readline = iternext(text.splitlines(True)) self.last_tokens = list(tokenize.generate_tokens(readline)) return self.last_tokens # Create our generate_tokens cache as a callable replacement function. generate_tokens = CachedTokenizer().generate_tokens COOKIE_RE = re.compile(r"^[ \t]*#.*coding[:=][ \t]*([-\w.]+)", flags=re.MULTILINE) @contract(source='bytes') def _source_encoding_py2(source): """Determine the encoding for `source`, according to PEP 263. `source` is a byte string, the text of the program. Returns a string, the name of the encoding. """ assert isinstance(source, bytes) # Do this so the detect_encode code we copied will work. readline = iternext(source.splitlines(True)) # This is mostly code adapted from Py3.2's tokenize module. def _get_normal_name(orig_enc): """Imitates get_normal_name in tokenizer.c.""" # Only care about the first 12 characters. enc = orig_enc[:12].lower().replace("_", "-") if re.match(r"^utf-8($|-)", enc): return "utf-8" if re.match(r"^(latin-1|iso-8859-1|iso-latin-1)($|-)", enc): return "iso-8859-1" return orig_enc # From detect_encode(): # It detects the encoding from the presence of a UTF-8 BOM or an encoding # cookie as specified in PEP-0263. If both a BOM and a cookie are present, # but disagree, a SyntaxError will be raised. If the encoding cookie is an # invalid charset, raise a SyntaxError. Note that if a UTF-8 BOM is found, # 'utf-8-sig' is returned. # If no encoding is specified, then the default will be returned. default = 'ascii' bom_found = False encoding = None def read_or_stop(): """Get the next source line, or ''.""" try: return readline() except StopIteration: return '' def find_cookie(line): """Find an encoding cookie in `line`.""" try: line_string = line.decode('ascii') except UnicodeDecodeError: return None matches = COOKIE_RE.findall(line_string) if not matches: return None encoding = _get_normal_name(matches[0]) try: codec = codecs.lookup(encoding) except LookupError: # This behavior mimics the Python interpreter raise SyntaxError("unknown encoding: " + encoding) if bom_found: # codecs in 2.3 were raw tuples of functions, assume the best. codec_name = getattr(codec, 'name', encoding) if codec_name != 'utf-8': # This behavior mimics the Python interpreter raise SyntaxError('encoding problem: utf-8') encoding += '-sig' return encoding first = read_or_stop() if first.startswith(codecs.BOM_UTF8): bom_found = True first = first[3:] default = 'utf-8-sig' if not first: return default encoding = find_cookie(first) if encoding: return encoding second = read_or_stop() if not second: return default encoding = find_cookie(second) if encoding: return encoding return default @contract(source='bytes') def _source_encoding_py3(source): """Determine the encoding for `source`, according to PEP 263. `source` is a byte string: the text of the program. Returns a string, the name of the encoding. """ readline = iternext(source.splitlines(True)) return tokenize.detect_encoding(readline)[0] if env.PY3: source_encoding = _source_encoding_py3 else: source_encoding = _source_encoding_py2 @contract(source='unicode') def compile_unicode(source, filename, mode): """Just like the `compile` builtin, but works on any Unicode string. Python 2's compile() builtin has a stupid restriction: if the source string is Unicode, then it may not have a encoding declaration in it. Why not? Who knows! It also decodes to utf8, and then tries to interpret those utf8 bytes according to the encoding declaration. Why? Who knows! This function neuters the coding declaration, and compiles it. """ source = neuter_encoding_declaration(source) if env.PY2 and isinstance(filename, unicode_class): filename = filename.encode(sys.getfilesystemencoding(), "replace") code = compile(source, filename, mode) return code @contract(source='unicode', returns='unicode') def neuter_encoding_declaration(source): """Return `source`, with any encoding declaration neutered.""" if COOKIE_RE.search(source): source_lines = source.splitlines(True) for lineno in range(min(2, len(source_lines))): source_lines[lineno] = COOKIE_RE.sub("# (deleted declaration)", source_lines[lineno]) source = "".join(source_lines) return source python-coverage-4.5+dfsg.1.orig/coverage/pytracer.py0000644000076600000620000002127013217546135020465 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Raw data collector for coverage.py.""" import atexit import dis import sys from coverage import env # We need the YIELD_VALUE opcode below, in a comparison-friendly form. YIELD_VALUE = dis.opmap['YIELD_VALUE'] if env.PY2: YIELD_VALUE = chr(YIELD_VALUE) class PyTracer(object): """Python implementation of the raw data tracer.""" # Because of poor implementations of trace-function-manipulating tools, # the Python trace function must be kept very simple. In particular, there # must be only one function ever set as the trace function, both through # sys.settrace, and as the return value from the trace function. Put # another way, the trace function must always return itself. It cannot # swap in other functions, or return None to avoid tracing a particular # frame. # # The trace manipulator that introduced this restriction is DecoratorTools, # which sets a trace function, and then later restores the pre-existing one # by calling sys.settrace with a function it found in the current frame. # # Systems that use DecoratorTools (or similar trace manipulations) must use # PyTracer to get accurate results. The command-line --timid argument is # used to force the use of this tracer. def __init__(self): # Attributes set from the collector: self.data = None self.trace_arcs = False self.should_trace = None self.should_trace_cache = None self.warn = None # The threading module to use, if any. self.threading = None self.cur_file_dict = None self.last_line = 0 # int, but uninitialized. self.cur_file_name = None self.data_stack = [] self.last_exc_back = None self.last_exc_firstlineno = 0 self.thread = None self.stopped = False self._activity = False self.in_atexit = False # On exit, self.in_atexit = True atexit.register(setattr, self, 'in_atexit', True) def __repr__(self): return "".format( id(self), sum(len(v) for v in self.data.values()), len(self.data), ) def log(self, marker, *args): """For hard-core logging of what this tracer is doing.""" with open("/tmp/debug_trace.txt", "a") as f: f.write("{} {:x}.{:x}[{}] {:x} {}\n".format( marker, id(self), self.thread.ident, len(self.data_stack), self.threading.currentThread().ident, " ".join(map(str, args)) )) def _trace(self, frame, event, arg_unused): """The trace function passed to sys.settrace.""" #self.log(":", frame.f_code.co_filename, frame.f_lineno, event) if (self.stopped and sys.gettrace() == self._trace): # The PyTrace.stop() method has been called, possibly by another # thread, let's deactivate ourselves now. #self.log("X", frame.f_code.co_filename, frame.f_lineno) sys.settrace(None) return None if self.last_exc_back: if frame == self.last_exc_back: # Someone forgot a return event. if self.trace_arcs and self.cur_file_dict: pair = (self.last_line, -self.last_exc_firstlineno) self.cur_file_dict[pair] = None self.cur_file_dict, self.cur_file_name, self.last_line = self.data_stack.pop() self.last_exc_back = None if event == 'call': # Entering a new function context. Decide if we should trace # in this file. self._activity = True self.data_stack.append((self.cur_file_dict, self.cur_file_name, self.last_line)) filename = frame.f_code.co_filename self.cur_file_name = filename disp = self.should_trace_cache.get(filename) if disp is None: disp = self.should_trace(filename, frame) self.should_trace_cache[filename] = disp self.cur_file_dict = None if disp.trace: tracename = disp.source_filename if tracename not in self.data: self.data[tracename] = {} self.cur_file_dict = self.data[tracename] # The call event is really a "start frame" event, and happens for # function calls and re-entering generators. The f_lasti field is # -1 for calls, and a real offset for generators. Use <0 as the # line number for calls, and the real line number for generators. if getattr(frame, 'f_lasti', -1) < 0: self.last_line = -frame.f_code.co_firstlineno else: self.last_line = frame.f_lineno elif event == 'line': # Record an executed line. if self.cur_file_dict is not None: lineno = frame.f_lineno #if frame.f_code.co_filename != self.cur_file_name: # self.log("*", frame.f_code.co_filename, self.cur_file_name, lineno) if self.trace_arcs: self.cur_file_dict[(self.last_line, lineno)] = None else: self.cur_file_dict[lineno] = None self.last_line = lineno elif event == 'return': if self.trace_arcs and self.cur_file_dict: # Record an arc leaving the function, but beware that a # "return" event might just mean yielding from a generator. # Jython seems to have an empty co_code, so just assume return. code = frame.f_code.co_code if (not code) or code[frame.f_lasti] != YIELD_VALUE: first = frame.f_code.co_firstlineno self.cur_file_dict[(self.last_line, -first)] = None # Leaving this function, pop the filename stack. self.cur_file_dict, self.cur_file_name, self.last_line = self.data_stack.pop() elif event == 'exception': self.last_exc_back = frame.f_back self.last_exc_firstlineno = frame.f_code.co_firstlineno return self._trace def start(self): """Start this Tracer. Return a Python function suitable for use with sys.settrace(). """ self.stopped = False if self.threading: if self.thread is None: self.thread = self.threading.currentThread() else: if self.thread.ident != self.threading.currentThread().ident: # Re-starting from a different thread!? Don't set the trace # function, but we are marked as running again, so maybe it # will be ok? #self.log("~", "starting on different threads") return self._trace sys.settrace(self._trace) return self._trace def stop(self): """Stop this Tracer.""" # Get the activate tracer callback before setting the stop flag to be # able to detect if the tracer was changed prior to stopping it. tf = sys.gettrace() # Set the stop flag. The actual call to sys.settrace(None) will happen # in the self._trace callback itself to make sure to call it from the # right thread. self.stopped = True if self.threading and self.thread.ident != self.threading.currentThread().ident: # Called on a different thread than started us: we can't unhook # ourselves, but we've set the flag that we should stop, so we # won't do any more tracing. #self.log("~", "stopping on different threads") return if self.warn: # PyPy clears the trace function before running atexit functions, # so don't warn if we are in atexit on PyPy and the trace function # has changed to None. dont_warn = (env.PYPY and env.PYPYVERSION >= (5, 4) and self.in_atexit and tf is None) if (not dont_warn) and tf != self._trace: self.warn( "Trace function changed, measurement is likely wrong: %r" % (tf,), slug="trace-changed", ) def activity(self): """Has there been any activity?""" return self._activity def reset_activity(self): """Reset the activity() flag.""" self._activity = False def get_stats(self): """Return a dictionary of statistics, or None.""" return None python-coverage-4.5+dfsg.1.orig/coverage/collector.py0000644000076600000620000004000513220613607020610 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Raw data collector for coverage.py.""" import os import sys from coverage import env from coverage.backward import litems, range # pylint: disable=redefined-builtin from coverage.debug import short_stack from coverage.files import abs_file from coverage.misc import CoverageException, isolate_module from coverage.pytracer import PyTracer os = isolate_module(os) try: # Use the C extension code when we can, for speed. from coverage.tracer import CTracer, CFileDisposition except ImportError: # Couldn't import the C extension, maybe it isn't built. if os.getenv('COVERAGE_TEST_TRACER') == 'c': # During testing, we use the COVERAGE_TEST_TRACER environment variable # to indicate that we've fiddled with the environment to test this # fallback code. If we thought we had a C tracer, but couldn't import # it, then exit quickly and clearly instead of dribbling confusing # errors. I'm using sys.exit here instead of an exception because an # exception here causes all sorts of other noise in unittest. sys.stderr.write("*** COVERAGE_TEST_TRACER is 'c' but can't import CTracer!\n") sys.exit(1) CTracer = None class FileDisposition(object): """A simple value type for recording what to do with a file.""" pass def should_start_context(frame): """Who-Tests-What hack: Determine whether this frame begins a new who-context.""" fn_name = frame.f_code.co_name if fn_name.startswith("test"): return fn_name return None class Collector(object): """Collects trace data. Creates a Tracer object for each thread, since they track stack information. Each Tracer points to the same shared data, contributing traced data points. When the Collector is started, it creates a Tracer for the current thread, and installs a function to create Tracers for each new thread started. When the Collector is stopped, all active Tracers are stopped. Threads started while the Collector is stopped will never have Tracers associated with them. """ # The stack of active Collectors. Collectors are added here when started, # and popped when stopped. Collectors on the stack are paused when not # the top, and resumed when they become the top again. _collectors = [] # The concurrency settings we support here. SUPPORTED_CONCURRENCIES = set(["greenlet", "eventlet", "gevent", "thread"]) def __init__(self, should_trace, check_include, timid, branch, warn, concurrency): """Create a collector. `should_trace` is a function, taking a file name and a frame, and returning a `coverage.FileDisposition object`. `check_include` is a function taking a file name and a frame. It returns a boolean: True if the file should be traced, False if not. If `timid` is true, then a slower simpler trace function will be used. This is important for some environments where manipulation of tracing functions make the faster more sophisticated trace function not operate properly. If `branch` is true, then branches will be measured. This involves collecting data on which statements followed each other (arcs). Use `get_arc_data` to get the arc data. `warn` is a warning function, taking a single string message argument and an optional slug argument which will be a string or None, to be used if a warning needs to be issued. `concurrency` is a list of strings indicating the concurrency libraries in use. Valid values are "greenlet", "eventlet", "gevent", or "thread" (the default). Of these four values, only one can be supplied. Other values are ignored. """ self.should_trace = should_trace self.check_include = check_include self.warn = warn self.branch = branch self.threading = None self.origin = short_stack() self.concur_id_func = None # We can handle a few concurrency options here, but only one at a time. these_concurrencies = self.SUPPORTED_CONCURRENCIES.intersection(concurrency) if len(these_concurrencies) > 1: raise CoverageException("Conflicting concurrency settings: %s" % concurrency) self.concurrency = these_concurrencies.pop() if these_concurrencies else '' try: if self.concurrency == "greenlet": import greenlet self.concur_id_func = greenlet.getcurrent elif self.concurrency == "eventlet": import eventlet.greenthread # pylint: disable=import-error,useless-suppression self.concur_id_func = eventlet.greenthread.getcurrent elif self.concurrency == "gevent": import gevent # pylint: disable=import-error,useless-suppression self.concur_id_func = gevent.getcurrent elif self.concurrency == "thread" or not self.concurrency: # It's important to import threading only if we need it. If # it's imported early, and the program being measured uses # gevent, then gevent's monkey-patching won't work properly. import threading self.threading = threading else: raise CoverageException("Don't understand concurrency=%s" % concurrency) except ImportError: raise CoverageException( "Couldn't trace with concurrency=%s, the module isn't installed." % ( self.concurrency, ) ) # Who-Tests-What is just a hack at the moment, so turn it on with an # environment variable. self.wtw = int(os.getenv('COVERAGE_WTW', 0)) self.reset() if timid: # Being timid: use the simple Python trace function. self._trace_class = PyTracer else: # Being fast: use the C Tracer if it is available, else the Python # trace function. self._trace_class = CTracer or PyTracer if self._trace_class is CTracer: self.file_disposition_class = CFileDisposition self.supports_plugins = True else: self.file_disposition_class = FileDisposition self.supports_plugins = False def __repr__(self): return "" % (id(self), self.tracer_name()) def tracer_name(self): """Return the class name of the tracer we're using.""" return self._trace_class.__name__ def _clear_data(self): """Clear out existing data, but stay ready for more collection.""" self.data.clear() for tracer in self.tracers: tracer.reset_activity() def reset(self): """Clear collected data, and prepare to collect more.""" # A dictionary mapping file names to dicts with line number keys (if not # branch coverage), or mapping file names to dicts with line number # pairs as keys (if branch coverage). self.data = {} # A dict mapping contexts to data dictionaries. self.contexts = {} self.contexts[None] = self.data # A dictionary mapping file names to file tracer plugin names that will # handle them. self.file_tracers = {} # The .should_trace_cache attribute is a cache from file names to # coverage.FileDisposition objects, or None. When a file is first # considered for tracing, a FileDisposition is obtained from # Coverage.should_trace. Its .trace attribute indicates whether the # file should be traced or not. If it should be, a plugin with dynamic # file names can decide not to trace it based on the dynamic file name # being excluded by the inclusion rules, in which case the # FileDisposition will be replaced by None in the cache. if env.PYPY: import __pypy__ # pylint: disable=import-error # Alex Gaynor said: # should_trace_cache is a strictly growing key: once a key is in # it, it never changes. Further, the keys used to access it are # generally constant, given sufficient context. That is to say, at # any given point _trace() is called, pypy is able to know the key. # This is because the key is determined by the physical source code # line, and that's invariant with the call site. # # This property of a dict with immutable keys, combined with # call-site-constant keys is a match for PyPy's module dict, # which is optimized for such workloads. # # This gives a 20% benefit on the workload described at # https://bitbucket.org/pypy/pypy/issue/1871/10x-slower-than-cpython-under-coverage self.should_trace_cache = __pypy__.newdict("module") else: self.should_trace_cache = {} # Our active Tracers. self.tracers = [] self._clear_data() def _start_tracer(self): """Start a new Tracer object, and store it in self.tracers.""" tracer = self._trace_class() tracer.data = self.data tracer.trace_arcs = self.branch tracer.should_trace = self.should_trace tracer.should_trace_cache = self.should_trace_cache tracer.warn = self.warn if hasattr(tracer, 'concur_id_func'): tracer.concur_id_func = self.concur_id_func elif self.concur_id_func: raise CoverageException( "Can't support concurrency=%s with %s, only threads are supported" % ( self.concurrency, self.tracer_name(), ) ) if hasattr(tracer, 'file_tracers'): tracer.file_tracers = self.file_tracers if hasattr(tracer, 'threading'): tracer.threading = self.threading if hasattr(tracer, 'check_include'): tracer.check_include = self.check_include if self.wtw: if hasattr(tracer, 'should_start_context'): tracer.should_start_context = should_start_context if hasattr(tracer, 'switch_context'): tracer.switch_context = self.switch_context fn = tracer.start() self.tracers.append(tracer) return fn # The trace function has to be set individually on each thread before # execution begins. Ironically, the only support the threading module has # for running code before the thread main is the tracing function. So we # install this as a trace function, and the first time it's called, it does # the real trace installation. def _installation_trace(self, frame, event, arg): """Called on new threads, installs the real tracer.""" # Remove ourselves as the trace function. sys.settrace(None) # Install the real tracer. fn = self._start_tracer() # Invoke the real trace function with the current event, to be sure # not to lose an event. if fn: fn = fn(frame, event, arg) # Return the new trace function to continue tracing in this scope. return fn def start(self): """Start collecting trace information.""" if self._collectors: self._collectors[-1].pause() self.tracers = [] # Check to see whether we had a fullcoverage tracer installed. If so, # get the stack frames it stashed away for us. traces0 = [] fn0 = sys.gettrace() if fn0: tracer0 = getattr(fn0, '__self__', None) if tracer0: traces0 = getattr(tracer0, 'traces', []) try: # Install the tracer on this thread. fn = self._start_tracer() except: if self._collectors: self._collectors[-1].resume() raise # If _start_tracer succeeded, then we add ourselves to the global # stack of collectors. self._collectors.append(self) # Replay all the events from fullcoverage into the new trace function. for args in traces0: (frame, event, arg), lineno = args try: fn(frame, event, arg, lineno=lineno) except TypeError: raise Exception("fullcoverage must be run with the C trace function.") # Install our installation tracer in threading, to jump-start other # threads. if self.threading: self.threading.settrace(self._installation_trace) def stop(self): """Stop collecting trace information.""" assert self._collectors if self._collectors[-1] is not self: print("self._collectors:") for c in self._collectors: print(" {!r}\n{}".format(c, c.origin)) assert self._collectors[-1] is self, ( "Expected current collector to be %r, but it's %r" % (self, self._collectors[-1]) ) self.pause() # Remove this Collector from the stack, and resume the one underneath # (if any). self._collectors.pop() if self._collectors: self._collectors[-1].resume() def pause(self): """Pause tracing, but be prepared to `resume`.""" for tracer in self.tracers: tracer.stop() stats = tracer.get_stats() if stats: print("\nCoverage.py tracer stats:") for k in sorted(stats.keys()): print("%20s: %s" % (k, stats[k])) if self.threading: self.threading.settrace(None) def resume(self): """Resume tracing after a `pause`.""" for tracer in self.tracers: tracer.start() if self.threading: self.threading.settrace(self._installation_trace) else: self._start_tracer() def _activity(self): """Has any activity been traced? Returns a boolean, True if any trace function was invoked. """ return any(tracer.activity() for tracer in self.tracers) def switch_context(self, new_context): """Who-Tests-What hack: switch to a new who-context.""" # Make a new data dict, or find the existing one, and switch all the # tracers to use it. data = self.contexts.setdefault(new_context, {}) for tracer in self.tracers: tracer.data = data def save_data(self, covdata): """Save the collected data to a `CoverageData`. Returns True if there was data to save, False if not. """ if not self._activity(): return False def abs_file_dict(d): """Return a dict like d, but with keys modified by `abs_file`.""" # The call to litems() ensures that the GIL protects the dictionary # iterator against concurrent modifications by tracers running # in other threads. We try three times in case of concurrent # access, hoping to get a clean copy. runtime_err = None for _ in range(3): try: items = litems(d) except RuntimeError as ex: runtime_err = ex else: break else: raise runtime_err # pylint: disable=raising-bad-type return dict((abs_file(k), v) for k, v in items) if self.branch: covdata.add_arcs(abs_file_dict(self.data)) else: covdata.add_lines(abs_file_dict(self.data)) covdata.add_file_tracers(abs_file_dict(self.file_tracers)) if self.wtw: # Just a hack, so just hack it. import pprint out_file = "coverage_wtw_{:06}.py".format(os.getpid()) with open(out_file, "w") as wtw_out: pprint.pprint(self.contexts, wtw_out) self._clear_data() return True python-coverage-4.5+dfsg.1.orig/coverage/__main__.py0000644000076600000620000000040113146037571020345 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Coverage.py's main entry point.""" import sys from coverage.cmdline import main sys.exit(main()) python-coverage-4.5+dfsg.1.orig/coverage/report.py0000644000076600000620000000706413230766432020153 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Reporter foundation for coverage.py.""" import os import warnings from coverage.files import prep_patterns, FnmatchMatcher from coverage.misc import CoverageException, NoSource, NotPython, isolate_module os = isolate_module(os) class Reporter(object): """A base class for all reporters.""" def __init__(self, coverage, config): """Create a reporter. `coverage` is the coverage instance. `config` is an instance of CoverageConfig, for controlling all sorts of behavior. """ self.coverage = coverage self.config = config # The directory into which to place the report, used by some derived # classes. self.directory = None # Our method find_file_reporters used to set an attribute that other # code could read. That's been refactored away, but some third parties # were using that attribute. We'll continue to support it in a noisy # way for now. self._file_reporters = [] @property def file_reporters(self): """Keep .file_reporters working for private-grabbing tools.""" warnings.warn( "Report.file_reporters will no longer be available in Coverage.py 4.2", DeprecationWarning, ) return self._file_reporters def find_file_reporters(self, morfs): """Find the FileReporters we'll report on. `morfs` is a list of modules or file names. Returns a list of FileReporters. """ reporters = self.coverage._get_file_reporters(morfs) if self.config.report_include: matcher = FnmatchMatcher(prep_patterns(self.config.report_include)) reporters = [fr for fr in reporters if matcher.match(fr.filename)] if self.config.report_omit: matcher = FnmatchMatcher(prep_patterns(self.config.report_omit)) reporters = [fr for fr in reporters if not matcher.match(fr.filename)] self._file_reporters = sorted(reporters) return self._file_reporters def report_files(self, report_fn, morfs, directory=None): """Run a reporting function on a number of morfs. `report_fn` is called for each relative morf in `morfs`. It is called as:: report_fn(file_reporter, analysis) where `file_reporter` is the `FileReporter` for the morf, and `analysis` is the `Analysis` for the morf. """ file_reporters = self.find_file_reporters(morfs) if not file_reporters: raise CoverageException("No data to report.") self.directory = directory if self.directory and not os.path.exists(self.directory): os.makedirs(self.directory) for fr in file_reporters: try: report_fn(fr, self.coverage._analyze(fr)) except NoSource: if not self.config.ignore_errors: raise except NotPython: # Only report errors for .py files, and only if we didn't # explicitly suppress those errors. # NotPython is only raised by PythonFileReporter, which has a # should_be_python() method. if fr.should_be_python(): if self.config.ignore_errors: self.coverage._warn("Could not parse Python file {0}".format(fr.filename)) else: raise python-coverage-4.5+dfsg.1.orig/coverage/parser.py0000644000076600000620000013075613173515667020151 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Code parsing for coverage.py.""" import ast import collections import os import re import token import tokenize from coverage import env from coverage.backward import range # pylint: disable=redefined-builtin from coverage.backward import bytes_to_ints, string_class from coverage.bytecode import CodeObjects from coverage.debug import short_stack from coverage.misc import contract, join_regex, new_contract, nice_pair, one_of from coverage.misc import NoSource, NotPython, StopEverything from coverage.phystokens import compile_unicode, generate_tokens, neuter_encoding_declaration class PythonParser(object): """Parse code to find executable lines, excluded lines, etc. This information is all based on static analysis: no code execution is involved. """ @contract(text='unicode|None') def __init__(self, text=None, filename=None, exclude=None): """ Source can be provided as `text`, the text itself, or `filename`, from which the text will be read. Excluded lines are those that match `exclude`, a regex. """ assert text or filename, "PythonParser needs either text or filename" self.filename = filename or "" self.text = text if not self.text: from coverage.python import get_python_source try: self.text = get_python_source(self.filename) except IOError as err: raise NoSource( "No source for code: '%s': %s" % (self.filename, err) ) self.exclude = exclude # The text lines of the parsed code. self.lines = self.text.split('\n') # The normalized line numbers of the statements in the code. Exclusions # are taken into account, and statements are adjusted to their first # lines. self.statements = set() # The normalized line numbers of the excluded lines in the code, # adjusted to their first lines. self.excluded = set() # The raw_* attributes are only used in this class, and in # lab/parser.py to show how this class is working. # The line numbers that start statements, as reported by the line # number table in the bytecode. self.raw_statements = set() # The raw line numbers of excluded lines of code, as marked by pragmas. self.raw_excluded = set() # The line numbers of class and function definitions. self.raw_classdefs = set() # The line numbers of docstring lines. self.raw_docstrings = set() # Internal detail, used by lab/parser.py. self.show_tokens = False # A dict mapping line numbers to lexical statement starts for # multi-line statements. self._multiline = {} # Lazily-created ByteParser, arc data, and missing arc descriptions. self._byte_parser = None self._all_arcs = None self._missing_arc_fragments = None @property def byte_parser(self): """Create a ByteParser on demand.""" if not self._byte_parser: self._byte_parser = ByteParser(self.text, filename=self.filename) return self._byte_parser def lines_matching(self, *regexes): """Find the lines matching one of a list of regexes. Returns a set of line numbers, the lines that contain a match for one of the regexes in `regexes`. The entire line needn't match, just a part of it. """ combined = join_regex(regexes) if env.PY2: combined = combined.decode("utf8") regex_c = re.compile(combined) matches = set() for i, ltext in enumerate(self.lines, start=1): if regex_c.search(ltext): matches.add(i) return matches def _raw_parse(self): """Parse the source to find the interesting facts about its lines. A handful of attributes are updated. """ # Find lines which match an exclusion pattern. if self.exclude: self.raw_excluded = self.lines_matching(self.exclude) # Tokenize, to find excluded suites, to find docstrings, and to find # multi-line statements. indent = 0 exclude_indent = 0 excluding = False excluding_decorators = False prev_toktype = token.INDENT first_line = None empty = True first_on_line = True tokgen = generate_tokens(self.text) for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen: if self.show_tokens: # pragma: debugging print("%10s %5s %-20r %r" % ( tokenize.tok_name.get(toktype, toktype), nice_pair((slineno, elineno)), ttext, ltext )) if toktype == token.INDENT: indent += 1 elif toktype == token.DEDENT: indent -= 1 elif toktype == token.NAME: if ttext == 'class': # Class definitions look like branches in the bytecode, so # we need to exclude them. The simplest way is to note the # lines with the 'class' keyword. self.raw_classdefs.add(slineno) elif toktype == token.OP: if ttext == ':': should_exclude = (elineno in self.raw_excluded) or excluding_decorators if not excluding and should_exclude: # Start excluding a suite. We trigger off of the colon # token so that the #pragma comment will be recognized on # the same line as the colon. self.raw_excluded.add(elineno) exclude_indent = indent excluding = True excluding_decorators = False elif ttext == '@' and first_on_line: # A decorator. if elineno in self.raw_excluded: excluding_decorators = True if excluding_decorators: self.raw_excluded.add(elineno) elif toktype == token.STRING and prev_toktype == token.INDENT: # Strings that are first on an indented line are docstrings. # (a trick from trace.py in the stdlib.) This works for # 99.9999% of cases. For the rest (!) see: # http://stackoverflow.com/questions/1769332/x/1769794#1769794 self.raw_docstrings.update(range(slineno, elineno+1)) elif toktype == token.NEWLINE: if first_line is not None and elineno != first_line: # We're at the end of a line, and we've ended on a # different line than the first line of the statement, # so record a multi-line range. for l in range(first_line, elineno+1): self._multiline[l] = first_line first_line = None first_on_line = True if ttext.strip() and toktype != tokenize.COMMENT: # A non-whitespace token. empty = False if first_line is None: # The token is not whitespace, and is the first in a # statement. first_line = slineno # Check whether to end an excluded suite. if excluding and indent <= exclude_indent: excluding = False if excluding: self.raw_excluded.add(elineno) first_on_line = False prev_toktype = toktype # Find the starts of the executable statements. if not empty: self.raw_statements.update(self.byte_parser._find_statements()) def first_line(self, line): """Return the first line number of the statement including `line`.""" return self._multiline.get(line, line) def first_lines(self, lines): """Map the line numbers in `lines` to the correct first line of the statement. Returns a set of the first lines. """ return set(self.first_line(l) for l in lines) def translate_lines(self, lines): """Implement `FileReporter.translate_lines`.""" return self.first_lines(lines) def translate_arcs(self, arcs): """Implement `FileReporter.translate_arcs`.""" return [(self.first_line(a), self.first_line(b)) for (a, b) in arcs] def parse_source(self): """Parse source text to find executable lines, excluded lines, etc. Sets the .excluded and .statements attributes, normalized to the first line of multi-line statements. """ try: self._raw_parse() except (tokenize.TokenError, IndentationError) as err: if hasattr(err, "lineno"): lineno = err.lineno # IndentationError else: lineno = err.args[1][0] # TokenError raise NotPython( u"Couldn't parse '%s' as Python source: '%s' at line %d" % ( self.filename, err.args[0], lineno ) ) self.excluded = self.first_lines(self.raw_excluded) ignore = self.excluded | self.raw_docstrings starts = self.raw_statements - ignore self.statements = self.first_lines(starts) - ignore def arcs(self): """Get information about the arcs available in the code. Returns a set of line number pairs. Line numbers have been normalized to the first line of multi-line statements. """ if self._all_arcs is None: self._analyze_ast() return self._all_arcs def _analyze_ast(self): """Run the AstArcAnalyzer and save its results. `_all_arcs` is the set of arcs in the code. """ aaa = AstArcAnalyzer(self.text, self.raw_statements, self._multiline) aaa.analyze() self._all_arcs = set() for l1, l2 in aaa.arcs: fl1 = self.first_line(l1) fl2 = self.first_line(l2) if fl1 != fl2: self._all_arcs.add((fl1, fl2)) self._missing_arc_fragments = aaa.missing_arc_fragments def exit_counts(self): """Get a count of exits from that each line. Excluded lines are excluded. """ exit_counts = collections.defaultdict(int) for l1, l2 in self.arcs(): if l1 < 0: # Don't ever report -1 as a line number continue if l1 in self.excluded: # Don't report excluded lines as line numbers. continue if l2 in self.excluded: # Arcs to excluded lines shouldn't count. continue exit_counts[l1] += 1 # Class definitions have one extra exit, so remove one for each: for l in self.raw_classdefs: # Ensure key is there: class definitions can include excluded lines. if l in exit_counts: exit_counts[l] -= 1 return exit_counts def missing_arc_description(self, start, end, executed_arcs=None): """Provide an English sentence describing a missing arc.""" if self._missing_arc_fragments is None: self._analyze_ast() actual_start = start if ( executed_arcs and end < 0 and end == -start and (end, start) not in executed_arcs and (end, start) in self._missing_arc_fragments ): # It's a one-line callable, and we never even started it, # and we have a message about not starting it. start, end = end, start fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)]) msgs = [] for fragment_pair in fragment_pairs: smsg, emsg = fragment_pair if emsg is None: if end < 0: # Hmm, maybe we have a one-line callable, let's check. if (-end, end) in self._missing_arc_fragments: return self.missing_arc_description(-end, end) emsg = "didn't jump to the function exit" else: emsg = "didn't jump to line {lineno}" emsg = emsg.format(lineno=end) msg = "line {start} {emsg}".format(start=actual_start, emsg=emsg) if smsg is not None: msg += ", because {smsg}".format(smsg=smsg.format(lineno=actual_start)) msgs.append(msg) return " or ".join(msgs) class ByteParser(object): """Parse bytecode to understand the structure of code.""" @contract(text='unicode') def __init__(self, text, code=None, filename=None): self.text = text if code: self.code = code else: try: self.code = compile_unicode(text, filename, "exec") except SyntaxError as synerr: raise NotPython( u"Couldn't parse '%s' as Python source: '%s' at line %d" % ( filename, synerr.msg, synerr.lineno ) ) # Alternative Python implementations don't always provide all the # attributes on code objects that we need to do the analysis. for attr in ['co_lnotab', 'co_firstlineno']: if not hasattr(self.code, attr): raise StopEverything( # pragma: only jython "This implementation of Python doesn't support code analysis.\n" "Run coverage.py under another Python for this command." ) def child_parsers(self): """Iterate over all the code objects nested within this one. The iteration includes `self` as its first value. """ children = CodeObjects(self.code) return (ByteParser(self.text, code=c) for c in children) def _bytes_lines(self): """Map byte offsets to line numbers in `code`. Uses co_lnotab described in Python/compile.c to map byte offsets to line numbers. Produces a sequence: (b0, l0), (b1, l1), ... Only byte offsets that correspond to line numbers are included in the results. """ # Adapted from dis.py in the standard library. byte_increments = bytes_to_ints(self.code.co_lnotab[0::2]) line_increments = bytes_to_ints(self.code.co_lnotab[1::2]) last_line_num = None line_num = self.code.co_firstlineno byte_num = 0 for byte_incr, line_incr in zip(byte_increments, line_increments): if byte_incr: if line_num != last_line_num: yield (byte_num, line_num) last_line_num = line_num byte_num += byte_incr line_num += line_incr if line_num != last_line_num: yield (byte_num, line_num) def _find_statements(self): """Find the statements in `self.code`. Produce a sequence of line numbers that start statements. Recurses into all code objects reachable from `self.code`. """ for bp in self.child_parsers(): # Get all of the lineno information from this code. for _, l in bp._bytes_lines(): yield l # # AST analysis # class LoopBlock(object): """A block on the block stack representing a `for` or `while` loop.""" @contract(start=int) def __init__(self, start): # The line number where the loop starts. self.start = start # A set of ArcStarts, the arcs from break statements exiting this loop. self.break_exits = set() class FunctionBlock(object): """A block on the block stack representing a function definition.""" @contract(start=int, name=str) def __init__(self, start, name): # The line number where the function starts. self.start = start # The name of the function. self.name = name class TryBlock(object): """A block on the block stack representing a `try` block.""" @contract(handler_start='int|None', final_start='int|None') def __init__(self, handler_start, final_start): # The line number of the first "except" handler, if any. self.handler_start = handler_start # The line number of the "finally:" clause, if any. self.final_start = final_start # The ArcStarts for breaks/continues/returns/raises inside the "try:" # that need to route through the "finally:" clause. self.break_from = set() self.continue_from = set() self.return_from = set() self.raise_from = set() class ArcStart(collections.namedtuple("Arc", "lineno, cause")): """The information needed to start an arc. `lineno` is the line number the arc starts from. `cause` is an English text fragment used as the `startmsg` for AstArcAnalyzer.missing_arc_fragments. It will be used to describe why an arc wasn't executed, so should fit well into a sentence of the form, "Line 17 didn't run because {cause}." The fragment can include "{lineno}" to have `lineno` interpolated into it. """ def __new__(cls, lineno, cause=None): return super(ArcStart, cls).__new__(cls, lineno, cause) # Define contract words that PyContract doesn't have. # ArcStarts is for a list or set of ArcStart's. new_contract('ArcStarts', lambda seq: all(isinstance(x, ArcStart) for x in seq)) # Turn on AST dumps with an environment variable. AST_DUMP = bool(int(os.environ.get("COVERAGE_AST_DUMP", 0))) class NodeList(object): """A synthetic fictitious node, containing a sequence of nodes. This is used when collapsing optimized if-statements, to represent the unconditional execution of one of the clauses. """ def __init__(self, body): self.body = body self.lineno = body[0].lineno class AstArcAnalyzer(object): """Analyze source text with an AST to find executable code paths.""" @contract(text='unicode', statements=set) def __init__(self, text, statements, multiline): self.root_node = ast.parse(neuter_encoding_declaration(text)) # TODO: I think this is happening in too many places. self.statements = set(multiline.get(l, l) for l in statements) self.multiline = multiline if AST_DUMP: # pragma: debugging # Dump the AST so that failing tests have helpful output. print("Statements: {0}".format(self.statements)) print("Multiline map: {0}".format(self.multiline)) ast_dump(self.root_node) self.arcs = set() # A map from arc pairs to a list of pairs of sentence fragments: # { (start, end): [(startmsg, endmsg), ...], } # # For an arc from line 17, they should be usable like: # "Line 17 {endmsg}, because {startmsg}" self.missing_arc_fragments = collections.defaultdict(list) self.block_stack = [] self.debug = bool(int(os.environ.get("COVERAGE_TRACK_ARCS", 0))) def analyze(self): """Examine the AST tree from `root_node` to determine possible arcs. This sets the `arcs` attribute to be a set of (from, to) line number pairs. """ for node in ast.walk(self.root_node): node_name = node.__class__.__name__ code_object_handler = getattr(self, "_code_object__" + node_name, None) if code_object_handler is not None: code_object_handler(node) def add_arc(self, start, end, smsg=None, emsg=None): """Add an arc, including message fragments to use if it is missing.""" if self.debug: # pragma: debugging print("\nAdding arc: ({}, {}): {!r}, {!r}".format(start, end, smsg, emsg)) print(short_stack(limit=6)) self.arcs.add((start, end)) if smsg is not None or emsg is not None: self.missing_arc_fragments[(start, end)].append((smsg, emsg)) def nearest_blocks(self): """Yield the blocks in nearest-to-farthest order.""" return reversed(self.block_stack) @contract(returns=int) def line_for_node(self, node): """What is the right line number to use for this node? This dispatches to _line__Node functions where needed. """ node_name = node.__class__.__name__ handler = getattr(self, "_line__" + node_name, None) if handler is not None: return handler(node) else: return node.lineno def _line__Assign(self, node): return self.line_for_node(node.value) def _line__Dict(self, node): # Python 3.5 changed how dict literals are made. if env.PYVERSION >= (3, 5) and node.keys: if node.keys[0] is not None: return node.keys[0].lineno else: # Unpacked dict literals `{**{'a':1}}` have None as the key, # use the value in that case. return node.values[0].lineno else: return node.lineno def _line__List(self, node): if node.elts: return self.line_for_node(node.elts[0]) else: return node.lineno def _line__Module(self, node): if node.body: return self.line_for_node(node.body[0]) else: # Empty modules have no line number, they always start at 1. return 1 # The node types that just flow to the next node with no complications. OK_TO_DEFAULT = set([ "Assign", "Assert", "AugAssign", "Delete", "Exec", "Expr", "Global", "Import", "ImportFrom", "Nonlocal", "Pass", "Print", ]) @contract(returns='ArcStarts') def add_arcs(self, node): """Add the arcs for `node`. Return a set of ArcStarts, exits from this node to the next. Because a node represents an entire sub-tree (including its children), the exits from a node can be arbitrarily complex:: if something(1): if other(2): doit(3) else: doit(5) There are two exits from line 1: they start at line 3 and line 5. """ node_name = node.__class__.__name__ handler = getattr(self, "_handle__" + node_name, None) if handler is not None: return handler(node) else: # No handler: either it's something that's ok to default (a simple # statement), or it's something we overlooked. Change this 0 to 1 # to see if it's overlooked. if 0: if node_name not in self.OK_TO_DEFAULT: print("*** Unhandled: {0}".format(node)) # Default for simple statements: one exit from this node. return set([ArcStart(self.line_for_node(node))]) @one_of("from_start, prev_starts") @contract(returns='ArcStarts') def add_body_arcs(self, body, from_start=None, prev_starts=None): """Add arcs for the body of a compound statement. `body` is the body node. `from_start` is a single `ArcStart` that can be the previous line in flow before this body. `prev_starts` is a set of ArcStarts that can be the previous line. Only one of them should be given. Returns a set of ArcStarts, the exits from this body. """ if prev_starts is None: prev_starts = set([from_start]) for body_node in body: lineno = self.line_for_node(body_node) first_line = self.multiline.get(lineno, lineno) if first_line not in self.statements: body_node = self.find_non_missing_node(body_node) if body_node is None: continue lineno = self.line_for_node(body_node) for prev_start in prev_starts: self.add_arc(prev_start.lineno, lineno, prev_start.cause) prev_starts = self.add_arcs(body_node) return prev_starts def find_non_missing_node(self, node): """Search `node` looking for a child that has not been optimized away. This might return the node you started with, or it will work recursively to find a child node in self.statements. Returns a node, or None if none of the node remains. """ # This repeats work just done in add_body_arcs, but this duplication # means we can avoid a function call in the 99.9999% case of not # optimizing away statements. lineno = self.line_for_node(node) first_line = self.multiline.get(lineno, lineno) if first_line in self.statements: return node missing_fn = getattr(self, "_missing__" + node.__class__.__name__, None) if missing_fn: node = missing_fn(node) else: node = None return node def _missing__If(self, node): # If the if-node is missing, then one of its children might still be # here, but not both. So return the first of the two that isn't missing. # Use a NodeList to hold the clauses as a single node. non_missing = self.find_non_missing_node(NodeList(node.body)) if non_missing: return non_missing if node.orelse: return self.find_non_missing_node(NodeList(node.orelse)) return None def _missing__NodeList(self, node): # A NodeList might be a mixture of missing and present nodes. Find the # ones that are present. non_missing_children = [] for child in node.body: child = self.find_non_missing_node(child) if child is not None: non_missing_children.append(child) # Return the simplest representation of the present children. if not non_missing_children: return None if len(non_missing_children) == 1: return non_missing_children[0] return NodeList(non_missing_children) def is_constant_expr(self, node): """Is this a compile-time constant?""" node_name = node.__class__.__name__ if node_name in ["NameConstant", "Num"]: return "Num" elif node_name == "Name": if node.id in ["True", "False", "None", "__debug__"]: return "Name" return None # In the fullness of time, these might be good tests to write: # while EXPR: # while False: # listcomps hidden deep in other expressions # listcomps hidden in lists: x = [[i for i in range(10)]] # nested function definitions # Exit processing: process_*_exits # # These functions process the four kinds of jump exits: break, continue, # raise, and return. To figure out where an exit goes, we have to look at # the block stack context. For example, a break will jump to the nearest # enclosing loop block, or the nearest enclosing finally block, whichever # is nearer. @contract(exits='ArcStarts') def process_break_exits(self, exits): """Add arcs due to jumps from `exits` being breaks.""" for block in self.nearest_blocks(): if isinstance(block, LoopBlock): block.break_exits.update(exits) break elif isinstance(block, TryBlock) and block.final_start is not None: block.break_from.update(exits) break @contract(exits='ArcStarts') def process_continue_exits(self, exits): """Add arcs due to jumps from `exits` being continues.""" for block in self.nearest_blocks(): if isinstance(block, LoopBlock): for xit in exits: self.add_arc(xit.lineno, block.start, xit.cause) break elif isinstance(block, TryBlock) and block.final_start is not None: block.continue_from.update(exits) break @contract(exits='ArcStarts') def process_raise_exits(self, exits): """Add arcs due to jumps from `exits` being raises.""" for block in self.nearest_blocks(): if isinstance(block, TryBlock): if block.handler_start is not None: for xit in exits: self.add_arc(xit.lineno, block.handler_start, xit.cause) break elif block.final_start is not None: block.raise_from.update(exits) break elif isinstance(block, FunctionBlock): for xit in exits: self.add_arc( xit.lineno, -block.start, xit.cause, "didn't except from function '{0}'".format(block.name), ) break @contract(exits='ArcStarts') def process_return_exits(self, exits): """Add arcs due to jumps from `exits` being returns.""" for block in self.nearest_blocks(): if isinstance(block, TryBlock) and block.final_start is not None: block.return_from.update(exits) break elif isinstance(block, FunctionBlock): for xit in exits: self.add_arc( xit.lineno, -block.start, xit.cause, "didn't return from function '{0}'".format(block.name), ) break # Handlers: _handle__* # # Each handler deals with a specific AST node type, dispatched from # add_arcs. Each deals with a particular kind of node type, and returns # the set of exits from that node. These functions mirror the Python # semantics of each syntactic construct. See the docstring for add_arcs to # understand the concept of exits from a node. @contract(returns='ArcStarts') def _handle__Break(self, node): here = self.line_for_node(node) break_start = ArcStart(here, cause="the break on line {lineno} wasn't executed") self.process_break_exits([break_start]) return set() @contract(returns='ArcStarts') def _handle_decorated(self, node): """Add arcs for things that can be decorated (classes and functions).""" last = self.line_for_node(node) if node.decorator_list: for dec_node in node.decorator_list: dec_start = self.line_for_node(dec_node) if dec_start != last: self.add_arc(last, dec_start) last = dec_start # The definition line may have been missed, but we should have it # in `self.statements`. For some constructs, `line_for_node` is # not what we'd think of as the first line in the statement, so map # it to the first one. body_start = self.line_for_node(node.body[0]) body_start = self.multiline.get(body_start, body_start) for lineno in range(last+1, body_start): if lineno in self.statements: self.add_arc(last, lineno) last = lineno # The body is handled in collect_arcs. return set([ArcStart(last)]) _handle__ClassDef = _handle_decorated @contract(returns='ArcStarts') def _handle__Continue(self, node): here = self.line_for_node(node) continue_start = ArcStart(here, cause="the continue on line {lineno} wasn't executed") self.process_continue_exits([continue_start]) return set() @contract(returns='ArcStarts') def _handle__For(self, node): start = self.line_for_node(node.iter) self.block_stack.append(LoopBlock(start=start)) from_start = ArcStart(start, cause="the loop on line {lineno} never started") exits = self.add_body_arcs(node.body, from_start=from_start) # Any exit from the body will go back to the top of the loop. for xit in exits: self.add_arc(xit.lineno, start, xit.cause) my_block = self.block_stack.pop() exits = my_block.break_exits from_start = ArcStart(start, cause="the loop on line {lineno} didn't complete") if node.orelse: else_exits = self.add_body_arcs(node.orelse, from_start=from_start) exits |= else_exits else: # No else clause: exit from the for line. exits.add(from_start) return exits _handle__AsyncFor = _handle__For _handle__FunctionDef = _handle_decorated _handle__AsyncFunctionDef = _handle_decorated @contract(returns='ArcStarts') def _handle__If(self, node): start = self.line_for_node(node.test) from_start = ArcStart(start, cause="the condition on line {lineno} was never true") exits = self.add_body_arcs(node.body, from_start=from_start) from_start = ArcStart(start, cause="the condition on line {lineno} was never false") exits |= self.add_body_arcs(node.orelse, from_start=from_start) return exits @contract(returns='ArcStarts') def _handle__NodeList(self, node): start = self.line_for_node(node) exits = self.add_body_arcs(node.body, from_start=ArcStart(start)) return exits @contract(returns='ArcStarts') def _handle__Raise(self, node): here = self.line_for_node(node) raise_start = ArcStart(here, cause="the raise on line {lineno} wasn't executed") self.process_raise_exits([raise_start]) # `raise` statement jumps away, no exits from here. return set() @contract(returns='ArcStarts') def _handle__Return(self, node): here = self.line_for_node(node) return_start = ArcStart(here, cause="the return on line {lineno} wasn't executed") self.process_return_exits([return_start]) # `return` statement jumps away, no exits from here. return set() @contract(returns='ArcStarts') def _handle__Try(self, node): if node.handlers: handler_start = self.line_for_node(node.handlers[0]) else: handler_start = None if node.finalbody: final_start = self.line_for_node(node.finalbody[0]) else: final_start = None try_block = TryBlock(handler_start, final_start) self.block_stack.append(try_block) start = self.line_for_node(node) exits = self.add_body_arcs(node.body, from_start=ArcStart(start)) # We're done with the `try` body, so this block no longer handles # exceptions. We keep the block so the `finally` clause can pick up # flows from the handlers and `else` clause. if node.finalbody: try_block.handler_start = None if node.handlers: # If there are `except` clauses, then raises in the try body # will already jump to them. Start this set over for raises in # `except` and `else`. try_block.raise_from = set([]) else: self.block_stack.pop() handler_exits = set() if node.handlers: last_handler_start = None for handler_node in node.handlers: handler_start = self.line_for_node(handler_node) if last_handler_start is not None: self.add_arc(last_handler_start, handler_start) last_handler_start = handler_start from_cause = "the exception caught by line {lineno} didn't happen" from_start = ArcStart(handler_start, cause=from_cause) handler_exits |= self.add_body_arcs(handler_node.body, from_start=from_start) if node.orelse: exits = self.add_body_arcs(node.orelse, prev_starts=exits) exits |= handler_exits if node.finalbody: self.block_stack.pop() final_from = ( # You can get to the `finally` clause from: exits | # the exits of the body or `else` clause, try_block.break_from | # or a `break`, try_block.continue_from | # or a `continue`, try_block.raise_from | # or a `raise`, try_block.return_from # or a `return`. ) final_exits = self.add_body_arcs(node.finalbody, prev_starts=final_from) if try_block.break_from: self.process_break_exits( self._combine_finally_starts(try_block.break_from, final_exits) ) if try_block.continue_from: self.process_continue_exits( self._combine_finally_starts(try_block.continue_from, final_exits) ) if try_block.raise_from: self.process_raise_exits( self._combine_finally_starts(try_block.raise_from, final_exits) ) if try_block.return_from: self.process_return_exits( self._combine_finally_starts(try_block.return_from, final_exits) ) if exits: # The finally clause's exits are only exits for the try block # as a whole if the try block had some exits to begin with. exits = final_exits return exits @contract(starts='ArcStarts', exits='ArcStarts', returns='ArcStarts') def _combine_finally_starts(self, starts, exits): """Helper for building the cause of `finally` branches. "finally" clauses might not execute their exits, and the causes could be due to a failure to execute any of the exits in the try block. So we use the causes from `starts` as the causes for `exits`. """ causes = [] for start in sorted(starts): if start.cause is not None: causes.append(start.cause.format(lineno=start.lineno)) cause = " or ".join(causes) exits = set(ArcStart(xit.lineno, cause) for xit in exits) return exits @contract(returns='ArcStarts') def _handle__TryExcept(self, node): # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get # TryExcept, it means there was no finally, so fake it, and treat as # a general Try node. node.finalbody = [] return self._handle__Try(node) @contract(returns='ArcStarts') def _handle__TryFinally(self, node): # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get # TryFinally, see if there's a TryExcept nested inside. If so, merge # them. Otherwise, fake fields to complete a Try node. node.handlers = [] node.orelse = [] first = node.body[0] if first.__class__.__name__ == "TryExcept" and node.lineno == first.lineno: assert len(node.body) == 1 node.body = first.body node.handlers = first.handlers node.orelse = first.orelse return self._handle__Try(node) @contract(returns='ArcStarts') def _handle__While(self, node): constant_test = self.is_constant_expr(node.test) start = to_top = self.line_for_node(node.test) if constant_test and (env.PY3 or constant_test == "Num"): to_top = self.line_for_node(node.body[0]) self.block_stack.append(LoopBlock(start=to_top)) from_start = ArcStart(start, cause="the condition on line {lineno} was never true") exits = self.add_body_arcs(node.body, from_start=from_start) for xit in exits: self.add_arc(xit.lineno, to_top, xit.cause) exits = set() my_block = self.block_stack.pop() exits.update(my_block.break_exits) from_start = ArcStart(start, cause="the condition on line {lineno} was never false") if node.orelse: else_exits = self.add_body_arcs(node.orelse, from_start=from_start) exits |= else_exits else: # No `else` clause: you can exit from the start. if not constant_test: exits.add(from_start) return exits @contract(returns='ArcStarts') def _handle__With(self, node): start = self.line_for_node(node) exits = self.add_body_arcs(node.body, from_start=ArcStart(start)) return exits _handle__AsyncWith = _handle__With def _code_object__Module(self, node): start = self.line_for_node(node) if node.body: exits = self.add_body_arcs(node.body, from_start=ArcStart(-start)) for xit in exits: self.add_arc(xit.lineno, -start, xit.cause, "didn't exit the module") else: # Empty module. self.add_arc(-start, start) self.add_arc(start, -start) def _code_object__FunctionDef(self, node): start = self.line_for_node(node) self.block_stack.append(FunctionBlock(start=start, name=node.name)) exits = self.add_body_arcs(node.body, from_start=ArcStart(-start)) self.process_return_exits(exits) self.block_stack.pop() _code_object__AsyncFunctionDef = _code_object__FunctionDef def _code_object__ClassDef(self, node): start = self.line_for_node(node) self.add_arc(-start, start) exits = self.add_body_arcs(node.body, from_start=ArcStart(start)) for xit in exits: self.add_arc( xit.lineno, -start, xit.cause, "didn't exit the body of class '{0}'".format(node.name), ) def _make_oneline_code_method(noun): # pylint: disable=no-self-argument """A function to make methods for online callable _code_object__ methods.""" def _code_object__oneline_callable(self, node): start = self.line_for_node(node) self.add_arc(-start, start, None, "didn't run the {0} on line {1}".format(noun, start)) self.add_arc( start, -start, None, "didn't finish the {0} on line {1}".format(noun, start), ) return _code_object__oneline_callable _code_object__Lambda = _make_oneline_code_method("lambda") _code_object__GeneratorExp = _make_oneline_code_method("generator expression") _code_object__DictComp = _make_oneline_code_method("dictionary comprehension") _code_object__SetComp = _make_oneline_code_method("set comprehension") if env.PY3: _code_object__ListComp = _make_oneline_code_method("list comprehension") if AST_DUMP: # pragma: debugging # Code only used when dumping the AST for debugging. SKIP_DUMP_FIELDS = ["ctx"] def _is_simple_value(value): """Is `value` simple enough to be displayed on a single line?""" return ( value in [None, [], (), {}, set()] or isinstance(value, (string_class, int, float)) ) def ast_dump(node, depth=0): """Dump the AST for `node`. This recursively walks the AST, printing a readable version. """ indent = " " * depth if not isinstance(node, ast.AST): print("{0}<{1} {2!r}>".format(indent, node.__class__.__name__, node)) return lineno = getattr(node, "lineno", None) if lineno is not None: linemark = " @ {0}".format(node.lineno) else: linemark = "" head = "{0}<{1}{2}".format(indent, node.__class__.__name__, linemark) named_fields = [ (name, value) for name, value in ast.iter_fields(node) if name not in SKIP_DUMP_FIELDS ] if not named_fields: print("{0}>".format(head)) elif len(named_fields) == 1 and _is_simple_value(named_fields[0][1]): field_name, value = named_fields[0] print("{0} {1}: {2!r}>".format(head, field_name, value)) else: print(head) if 0: print("{0}# mro: {1}".format( indent, ", ".join(c.__name__ for c in node.__class__.__mro__[1:]), )) next_indent = indent + " " for field_name, value in named_fields: prefix = "{0}{1}:".format(next_indent, field_name) if _is_simple_value(value): print("{0} {1!r}".format(prefix, value)) elif isinstance(value, list): print("{0} [".format(prefix)) for n in value: ast_dump(n, depth + 8) print("{0}]".format(next_indent)) else: print(prefix) ast_dump(value, depth + 8) print("{0}>".format(indent)) python-coverage-4.5+dfsg.1.orig/coverage/backward.py0000644000076600000620000001220413177624110020402 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Add things to old Pythons so I can pretend they are newer.""" # This file does tricky stuff, so disable a pylint warning. # pylint: disable=unused-import import sys from coverage import env # Pythons 2 and 3 differ on where to get StringIO. try: from cStringIO import StringIO except ImportError: from io import StringIO # In py3, ConfigParser was renamed to the more-standard configparser. # But there's a py3 backport that installs "configparser" in py2, and I don't # want it because it has annoying deprecation warnings. So try the real py2 # import first. try: import ConfigParser as configparser except ImportError: import configparser # What's a string called? try: string_class = basestring except NameError: string_class = str # What's a Unicode string called? try: unicode_class = unicode except NameError: unicode_class = str # Where do pickles come from? try: import cPickle as pickle except ImportError: import pickle # range or xrange? try: range = xrange # pylint: disable=redefined-builtin except NameError: range = range # shlex.quote is new, but there's an undocumented implementation in "pipes", # who knew!? try: from shlex import quote as shlex_quote except ImportError: # Useful function, available under a different (undocumented) name # in Python versions earlier than 3.3. from pipes import quote as shlex_quote # A function to iterate listlessly over a dict's items, and one to get the # items as a list. try: {}.iteritems except AttributeError: # Python 3 def iitems(d): """Produce the items from dict `d`.""" return d.items() def litems(d): """Return a list of items from dict `d`.""" return list(d.items()) else: # Python 2 def iitems(d): """Produce the items from dict `d`.""" return d.iteritems() def litems(d): """Return a list of items from dict `d`.""" return d.items() # Getting the `next` function from an iterator is different in 2 and 3. try: iter([]).next except AttributeError: def iternext(seq): """Get the `next` function for iterating over `seq`.""" return iter(seq).__next__ else: def iternext(seq): """Get the `next` function for iterating over `seq`.""" return iter(seq).next # Python 3.x is picky about bytes and strings, so provide methods to # get them right, and make them no-ops in 2.x if env.PY3: def to_bytes(s): """Convert string `s` to bytes.""" return s.encode('utf8') def binary_bytes(byte_values): """Produce a byte string with the ints from `byte_values`.""" return bytes(byte_values) def bytes_to_ints(bytes_value): """Turn a bytes object into a sequence of ints.""" # In Python 3, iterating bytes gives ints. return bytes_value else: def to_bytes(s): """Convert string `s` to bytes (no-op in 2.x).""" return s def binary_bytes(byte_values): """Produce a byte string with the ints from `byte_values`.""" return "".join(chr(b) for b in byte_values) def bytes_to_ints(bytes_value): """Turn a bytes object into a sequence of ints.""" for byte in bytes_value: yield ord(byte) try: # In Python 2.x, the builtins were in __builtin__ BUILTINS = sys.modules['__builtin__'] except KeyError: # In Python 3.x, they're in builtins BUILTINS = sys.modules['builtins'] # imp was deprecated in Python 3.3 try: import importlib import importlib.util imp = None except ImportError: importlib = None # We only want to use importlib if it has everything we need. try: importlib_util_find_spec = importlib.util.find_spec except Exception: import imp importlib_util_find_spec = None # What is the .pyc magic number for this version of Python? try: PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER except AttributeError: PYC_MAGIC_NUMBER = imp.get_magic() def invalidate_import_caches(): """Invalidate any import caches that may or may not exist.""" if importlib and hasattr(importlib, "invalidate_caches"): importlib.invalidate_caches() def import_local_file(modname, modfile=None): """Import a local file as a module. Opens a file in the current directory named `modname`.py, imports it as `modname`, and returns the module object. `modfile` is the file to import if it isn't in the current directory. """ try: from importlib.machinery import SourceFileLoader except ImportError: SourceFileLoader = None if modfile is None: modfile = modname + '.py' if SourceFileLoader: mod = SourceFileLoader(modname, modfile).load_module() else: for suff in imp.get_suffixes(): # pragma: part covered if suff[0] == '.py': break with open(modfile, 'r') as f: # pylint: disable=undefined-loop-variable mod = imp.load_module(modname, f, modfile, suff) return mod python-coverage-4.5+dfsg.1.orig/coverage/htmlfiles/0000755000076600000620000000000013237000174020235 5ustar staffpython-coverage-4.5+dfsg.1.orig/coverage/htmlfiles/jquery.hotkeys.js0000644000076600000620000000577113146037571023623 0ustar staff/* * jQuery Hotkeys Plugin * Copyright 2010, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * * Based upon the plugin by Tzury Bar Yochay: * http://github.com/tzuryby/hotkeys * * Original idea by: * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ */ (function(jQuery){ jQuery.hotkeys = { version: "0.8", specialKeys: { 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause", 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home", 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del", 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/", 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8", 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta" }, shiftNums: { "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<", ".": ">", "/": "?", "\\": "|" } }; function keyHandler( handleObj ) { // Only care when a possible input has been specified if ( typeof handleObj.data !== "string" ) { return; } var origHandler = handleObj.handler, keys = handleObj.data.toLowerCase().split(" "); handleObj.handler = function( event ) { // Don't fire in text-accepting inputs that we didn't directly bind to if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) || event.target.type === "text") ) { return; } // Keypress represents characters, not special keys var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ], character = String.fromCharCode( event.which ).toLowerCase(), key, modif = "", possible = {}; // check combinations (alt|ctrl|shift+anything) if ( event.altKey && special !== "alt" ) { modif += "alt+"; } if ( event.ctrlKey && special !== "ctrl" ) { modif += "ctrl+"; } // TODO: Need to make sure this works consistently across platforms if ( event.metaKey && !event.ctrlKey && special !== "meta" ) { modif += "meta+"; } if ( event.shiftKey && special !== "shift" ) { modif += "shift+"; } if ( special ) { possible[ modif + special ] = true; } else { possible[ modif + character ] = true; possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true; // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" if ( modif === "shift+" ) { possible[ jQuery.hotkeys.shiftNums[ character ] ] = true; } } for ( var i = 0, l = keys.length; i < l; i++ ) { if ( possible[ keys[i] ] ) { return origHandler.apply( this, arguments ); } } }; } jQuery.each([ "keydown", "keyup", "keypress" ], function() { jQuery.event.special[ this ] = { add: keyHandler }; }); })( jQuery ); python-coverage-4.5+dfsg.1.orig/coverage/htmlfiles/pyfile.html0000644000076600000620000000750713146037571022436 0ustar staff{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #} {# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt #} {# IE8 rounds line-height incorrectly, and adding this emulateIE7 line makes it right! #} {# http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/7684445e-f080-4d8f-8529-132763348e21 #} Coverage for {{fr.relative_filename|escape}}: {{nums.pc_covered_str}}% {% if extra_css %} {% endif %}
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

{% for line in lines -%}

{{line.number}}

{% endfor %}
{# These are the source lines, which are very sensitive to whitespace. -#} {# The `{ # - # }` below are comments which slurp up the following space. -#} {% for line in lines -%}

{#-#} {% if line.annotate -%} {{line.annotate}}{#-#} {{line.annotate_long}}{#-#} {% endif -%} {{line.html}} {#-#}

{% endfor %}
python-coverage-4.5+dfsg.1.orig/coverage/htmlfiles/keybd_open.png0000755000076600000620000000016013146037571023074 0ustar staffPNG  IHDR# B*17IDATxc  C Ƙ4 $?!dIS]T31 Q)7Ξ"VF"IENDB`python-coverage-4.5+dfsg.1.orig/coverage/htmlfiles/coverage_html.js0000644000076600000620000004403213146037571023427 0ustar staff// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 // For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt // Coverage.py HTML report browser code. /*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */ /*global coverage: true, document, window, $ */ coverage = {}; // Find all the elements with shortkey_* class, and use them to assign a shortcut key. coverage.assign_shortkeys = function () { $("*[class*='shortkey_']").each(function (i, e) { $.each($(e).attr("class").split(" "), function (i, c) { if (/^shortkey_/.test(c)) { $(document).bind('keydown', c.substr(9), function () { $(e).click(); }); } }); }); }; // Create the events for the help panel. coverage.wire_up_help_panel = function () { $("#keyboard_icon").click(function () { // Show the help panel, and position it so the keyboard icon in the // panel is in the same place as the keyboard icon in the header. $(".help_panel").show(); var koff = $("#keyboard_icon").offset(); var poff = $("#panel_icon").position(); $(".help_panel").offset({ top: koff.top-poff.top, left: koff.left-poff.left }); }); $("#panel_icon").click(function () { $(".help_panel").hide(); }); }; // Create the events for the filter box. coverage.wire_up_filter = function () { // Cache elements. var table = $("table.index"); var table_rows = table.find("tbody tr"); var table_row_names = table_rows.find("td.name a"); var no_rows = $("#no_rows"); // Create a duplicate table footer that we can modify with dynamic summed values. var table_footer = $("table.index tfoot tr"); var table_dynamic_footer = table_footer.clone(); table_dynamic_footer.attr('class', 'total_dynamic hidden'); table_footer.after(table_dynamic_footer); // Observe filter keyevents. $("#filter").on("keyup change", $.debounce(150, function (event) { var filter_value = $(this).val(); if (filter_value === "") { // Filter box is empty, remove all filtering. table_rows.removeClass("hidden"); // Show standard footer, hide dynamic footer. table_footer.removeClass("hidden"); table_dynamic_footer.addClass("hidden"); // Hide placeholder, show table. if (no_rows.length > 0) { no_rows.hide(); } table.show(); } else { // Filter table items by value. var hidden = 0; var shown = 0; // Hide / show elements. $.each(table_row_names, function () { var element = $(this).parents("tr"); if ($(this).text().indexOf(filter_value) === -1) { // hide element.addClass("hidden"); hidden++; } else { // show element.removeClass("hidden"); shown++; } }); // Show placeholder if no rows will be displayed. if (no_rows.length > 0) { if (shown === 0) { // Show placeholder, hide table. no_rows.show(); table.hide(); } else { // Hide placeholder, show table. no_rows.hide(); table.show(); } } // Manage dynamic header: if (hidden > 0) { // Calculate new dynamic sum values based on visible rows. for (var column = 2; column < 20; column++) { // Calculate summed value. var cells = table_rows.find('td:nth-child(' + column + ')'); if (!cells.length) { // No more columns...! break; } var sum = 0, numer = 0, denom = 0; $.each(cells.filter(':visible'), function () { var ratio = $(this).data("ratio"); if (ratio) { var splitted = ratio.split(" "); numer += parseInt(splitted[0], 10); denom += parseInt(splitted[1], 10); } else { sum += parseInt(this.innerHTML, 10); } }); // Get footer cell element. var footer_cell = table_dynamic_footer.find('td:nth-child(' + column + ')'); // Set value into dynamic footer cell element. if (cells[0].innerHTML.indexOf('%') > -1) { // Percentage columns use the numerator and denominator, // and adapt to the number of decimal places. var match = /\.([0-9]+)/.exec(cells[0].innerHTML); var places = 0; if (match) { places = match[1].length; } var pct = numer * 100 / denom; footer_cell.text(pct.toFixed(places) + '%'); } else { footer_cell.text(sum); } } // Hide standard footer, show dynamic footer. table_footer.addClass("hidden"); table_dynamic_footer.removeClass("hidden"); } else { // Show standard footer, hide dynamic footer. table_footer.removeClass("hidden"); table_dynamic_footer.addClass("hidden"); } } })); // Trigger change event on setup, to force filter on page refresh // (filter value may still be present). $("#filter").trigger("change"); }; // Loaded on index.html coverage.index_ready = function ($) { // Look for a cookie containing previous sort settings: var sort_list = []; var cookie_name = "COVERAGE_INDEX_SORT"; var i; // This almost makes it worth installing the jQuery cookie plugin: if (document.cookie.indexOf(cookie_name) > -1) { var cookies = document.cookie.split(";"); for (i = 0; i < cookies.length; i++) { var parts = cookies[i].split("="); if ($.trim(parts[0]) === cookie_name && parts[1]) { sort_list = eval("[[" + parts[1] + "]]"); break; } } } // Create a new widget which exists only to save and restore // the sort order: $.tablesorter.addWidget({ id: "persistentSort", // Format is called by the widget before displaying: format: function (table) { if (table.config.sortList.length === 0 && sort_list.length > 0) { // This table hasn't been sorted before - we'll use // our stored settings: $(table).trigger('sorton', [sort_list]); } else { // This is not the first load - something has // already defined sorting so we'll just update // our stored value to match: sort_list = table.config.sortList; } } }); // Configure our tablesorter to handle the variable number of // columns produced depending on report options: var headers = []; var col_count = $("table.index > thead > tr > th").length; headers[0] = { sorter: 'text' }; for (i = 1; i < col_count-1; i++) { headers[i] = { sorter: 'digit' }; } headers[col_count-1] = { sorter: 'percent' }; // Enable the table sorter: $("table.index").tablesorter({ widgets: ['persistentSort'], headers: headers }); coverage.assign_shortkeys(); coverage.wire_up_help_panel(); coverage.wire_up_filter(); // Watch for page unload events so we can save the final sort settings: $(window).unload(function () { document.cookie = cookie_name + "=" + sort_list.toString() + "; path=/"; }); }; // -- pyfile stuff -- coverage.pyfile_ready = function ($) { // If we're directed to a particular line number, highlight the line. var frag = location.hash; if (frag.length > 2 && frag[1] === 'n') { $(frag).addClass('highlight'); coverage.set_sel(parseInt(frag.substr(2), 10)); } else { coverage.set_sel(0); } $(document) .bind('keydown', 'j', coverage.to_next_chunk_nicely) .bind('keydown', 'k', coverage.to_prev_chunk_nicely) .bind('keydown', '0', coverage.to_top) .bind('keydown', '1', coverage.to_first_chunk) ; $(".button_toggle_run").click(function (evt) {coverage.toggle_lines(evt.target, "run");}); $(".button_toggle_exc").click(function (evt) {coverage.toggle_lines(evt.target, "exc");}); $(".button_toggle_mis").click(function (evt) {coverage.toggle_lines(evt.target, "mis");}); $(".button_toggle_par").click(function (evt) {coverage.toggle_lines(evt.target, "par");}); coverage.assign_shortkeys(); coverage.wire_up_help_panel(); coverage.init_scroll_markers(); // Rebuild scroll markers after window high changing $(window).resize(coverage.resize_scroll_markers); }; coverage.toggle_lines = function (btn, cls) { btn = $(btn); var hide = "hide_"+cls; if (btn.hasClass(hide)) { $("#source ."+cls).removeClass(hide); btn.removeClass(hide); } else { $("#source ."+cls).addClass(hide); btn.addClass(hide); } }; // Return the nth line div. coverage.line_elt = function (n) { return $("#t" + n); }; // Return the nth line number div. coverage.num_elt = function (n) { return $("#n" + n); }; // Return the container of all the code. coverage.code_container = function () { return $(".linenos"); }; // Set the selection. b and e are line numbers. coverage.set_sel = function (b, e) { // The first line selected. coverage.sel_begin = b; // The next line not selected. coverage.sel_end = (e === undefined) ? b+1 : e; }; coverage.to_top = function () { coverage.set_sel(0, 1); coverage.scroll_window(0); }; coverage.to_first_chunk = function () { coverage.set_sel(0, 1); coverage.to_next_chunk(); }; coverage.is_transparent = function (color) { // Different browsers return different colors for "none". return color === "transparent" || color === "rgba(0, 0, 0, 0)"; }; coverage.to_next_chunk = function () { var c = coverage; // Find the start of the next colored chunk. var probe = c.sel_end; var color, probe_line; while (true) { probe_line = c.line_elt(probe); if (probe_line.length === 0) { return; } color = probe_line.css("background-color"); if (!c.is_transparent(color)) { break; } probe++; } // There's a next chunk, `probe` points to it. var begin = probe; // Find the end of this chunk. var next_color = color; while (next_color === color) { probe++; probe_line = c.line_elt(probe); next_color = probe_line.css("background-color"); } c.set_sel(begin, probe); c.show_selection(); }; coverage.to_prev_chunk = function () { var c = coverage; // Find the end of the prev colored chunk. var probe = c.sel_begin-1; var probe_line = c.line_elt(probe); if (probe_line.length === 0) { return; } var color = probe_line.css("background-color"); while (probe > 0 && c.is_transparent(color)) { probe--; probe_line = c.line_elt(probe); if (probe_line.length === 0) { return; } color = probe_line.css("background-color"); } // There's a prev chunk, `probe` points to its last line. var end = probe+1; // Find the beginning of this chunk. var prev_color = color; while (prev_color === color) { probe--; probe_line = c.line_elt(probe); prev_color = probe_line.css("background-color"); } c.set_sel(probe+1, end); c.show_selection(); }; // Return the line number of the line nearest pixel position pos coverage.line_at_pos = function (pos) { var l1 = coverage.line_elt(1), l2 = coverage.line_elt(2), result; if (l1.length && l2.length) { var l1_top = l1.offset().top, line_height = l2.offset().top - l1_top, nlines = (pos - l1_top) / line_height; if (nlines < 1) { result = 1; } else { result = Math.ceil(nlines); } } else { result = 1; } return result; }; // Returns 0, 1, or 2: how many of the two ends of the selection are on // the screen right now? coverage.selection_ends_on_screen = function () { if (coverage.sel_begin === 0) { return 0; } var top = coverage.line_elt(coverage.sel_begin); var next = coverage.line_elt(coverage.sel_end-1); return ( (top.isOnScreen() ? 1 : 0) + (next.isOnScreen() ? 1 : 0) ); }; coverage.to_next_chunk_nicely = function () { coverage.finish_scrolling(); if (coverage.selection_ends_on_screen() === 0) { // The selection is entirely off the screen: select the top line on // the screen. var win = $(window); coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop())); } coverage.to_next_chunk(); }; coverage.to_prev_chunk_nicely = function () { coverage.finish_scrolling(); if (coverage.selection_ends_on_screen() === 0) { var win = $(window); coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop() + win.height())); } coverage.to_prev_chunk(); }; // Select line number lineno, or if it is in a colored chunk, select the // entire chunk coverage.select_line_or_chunk = function (lineno) { var c = coverage; var probe_line = c.line_elt(lineno); if (probe_line.length === 0) { return; } var the_color = probe_line.css("background-color"); if (!c.is_transparent(the_color)) { // The line is in a highlighted chunk. // Search backward for the first line. var probe = lineno; var color = the_color; while (probe > 0 && color === the_color) { probe--; probe_line = c.line_elt(probe); if (probe_line.length === 0) { break; } color = probe_line.css("background-color"); } var begin = probe + 1; // Search forward for the last line. probe = lineno; color = the_color; while (color === the_color) { probe++; probe_line = c.line_elt(probe); color = probe_line.css("background-color"); } coverage.set_sel(begin, probe); } else { coverage.set_sel(lineno); } }; coverage.show_selection = function () { var c = coverage; // Highlight the lines in the chunk c.code_container().find(".highlight").removeClass("highlight"); for (var probe = c.sel_begin; probe > 0 && probe < c.sel_end; probe++) { c.num_elt(probe).addClass("highlight"); } c.scroll_to_selection(); }; coverage.scroll_to_selection = function () { // Scroll the page if the chunk isn't fully visible. if (coverage.selection_ends_on_screen() < 2) { // Need to move the page. The html,body trick makes it scroll in all // browsers, got it from http://stackoverflow.com/questions/3042651 var top = coverage.line_elt(coverage.sel_begin); var top_pos = parseInt(top.offset().top, 10); coverage.scroll_window(top_pos - 30); } }; coverage.scroll_window = function (to_pos) { $("html,body").animate({scrollTop: to_pos}, 200); }; coverage.finish_scrolling = function () { $("html,body").stop(true, true); }; coverage.init_scroll_markers = function () { var c = coverage; // Init some variables c.lines_len = $('td.text p').length; c.body_h = $('body').height(); c.header_h = $('div#header').height(); c.missed_lines = $('td.text p.mis, td.text p.par'); // Build html c.resize_scroll_markers(); }; coverage.resize_scroll_markers = function () { var c = coverage, min_line_height = 3, max_line_height = 10, visible_window_h = $(window).height(); $('#scroll_marker').remove(); // Don't build markers if the window has no scroll bar. if (c.body_h <= visible_window_h) { return; } $("body").append("
 
"); var scroll_marker = $('#scroll_marker'), marker_scale = scroll_marker.height() / c.body_h, line_height = scroll_marker.height() / c.lines_len; // Line height must be between the extremes. if (line_height > min_line_height) { if (line_height > max_line_height) { line_height = max_line_height; } } else { line_height = min_line_height; } var previous_line = -99, last_mark, last_top; c.missed_lines.each(function () { var line_top = Math.round($(this).offset().top * marker_scale), id_name = $(this).attr('id'), line_number = parseInt(id_name.substring(1, id_name.length)); if (line_number === previous_line + 1) { // If this solid missed block just make previous mark higher. last_mark.css({ 'height': line_top + line_height - last_top }); } else { // Add colored line in scroll_marker block. scroll_marker.append('
'); last_mark = $('#m' + line_number); last_mark.css({ 'height': line_height, 'top': line_top }); last_top = line_top; } previous_line = line_number; }); }; python-coverage-4.5+dfsg.1.orig/coverage/htmlfiles/style.css0000644000076600000620000001514513146037571022127 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ /* CSS styles for coverage.py. */ /* Page-wide styles */ html, body, h1, h2, h3, p, table, td, th { margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; } /* Set baseline grid to 16 pt. */ body { font-family: georgia, serif; font-size: 1em; } html>body { font-size: 16px; } /* Set base font size to 12/16 */ p { font-size: .75em; /* 12/16 */ line-height: 1.33333333em; /* 16/12 */ } table { border-collapse: collapse; } td { vertical-align: top; } table tr.hidden { display: none !important; } p#no_rows { display: none; font-size: 1.2em; } a.nav { text-decoration: none; color: inherit; } a.nav:hover { text-decoration: underline; color: inherit; } /* Page structure */ #header { background: #f8f8f8; width: 100%; border-bottom: 1px solid #eee; } #source { padding: 1em; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } .indexfile #footer { margin: 1em 3em; } .pyfile #footer { margin: 1em 1em; } #footer .content { padding: 0; font-size: 85%; font-family: verdana, sans-serif; color: #666666; font-style: italic; } #index { margin: 1em 0 0 3em; } /* Header styles */ #header .content { padding: 1em 3em; } h1 { font-size: 1.25em; display: inline-block; } #filter_container { display: inline-block; float: right; margin: 0 2em 0 0; } #filter_container input { width: 10em; } h2.stats { margin-top: .5em; font-size: 1em; } .stats span { border: 1px solid; padding: .1em .25em; margin: 0 .1em; cursor: pointer; border-color: #999 #ccc #ccc #999; } .stats span.hide_run, .stats span.hide_exc, .stats span.hide_mis, .stats span.hide_par, .stats span.par.hide_run.hide_par { border-color: #ccc #999 #999 #ccc; } .stats span.par.hide_run { border-color: #999 #ccc #ccc #999; } .stats span.run { background: #ddffdd; } .stats span.exc { background: #eeeeee; } .stats span.mis { background: #ffdddd; } .stats span.hide_run { background: #eeffee; } .stats span.hide_exc { background: #f5f5f5; } .stats span.hide_mis { background: #ffeeee; } .stats span.par { background: #ffffaa; } .stats span.hide_par { background: #ffffcc; } /* Help panel */ #keyboard_icon { float: right; margin: 5px; cursor: pointer; } .help_panel { position: absolute; background: #ffffcc; padding: .5em; border: 1px solid #883; display: none; } .indexfile .help_panel { width: 20em; height: 4em; } .pyfile .help_panel { width: 16em; height: 8em; } .help_panel .legend { font-style: italic; margin-bottom: 1em; } #panel_icon { float: right; cursor: pointer; } .keyhelp { margin: .75em; } .keyhelp .key { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: monospace; font-weight: bold; background: #eee; } /* Source file styles */ .linenos p { text-align: right; margin: 0; padding: 0 .5em; color: #999999; font-family: verdana, sans-serif; font-size: .625em; /* 10/16 */ line-height: 1.6em; /* 16/10 */ } .linenos p.highlight { background: #ffdd00; } .linenos p a { text-decoration: none; color: #999999; } .linenos p a:hover { text-decoration: underline; color: #999999; } td.text { width: 100%; } .text p { margin: 0; padding: 0 0 0 .5em; border-left: 2px solid #ffffff; white-space: pre; position: relative; } .text p.mis { background: #ffdddd; border-left: 2px solid #ff0000; } .text p.run, .text p.run.hide_par { background: #ddffdd; border-left: 2px solid #00ff00; } .text p.exc { background: #eeeeee; border-left: 2px solid #808080; } .text p.par, .text p.par.hide_run { background: #ffffaa; border-left: 2px solid #eeee99; } .text p.hide_run, .text p.hide_exc, .text p.hide_mis, .text p.hide_par, .text p.hide_run.hide_par { background: inherit; } .text span.annotate { font-family: georgia; color: #666; float: right; padding-right: .5em; } .text p.hide_par span.annotate { display: none; } .text span.annotate.long { display: none; } .text p:hover span.annotate.long { display: block; max-width: 50%; white-space: normal; float: right; position: absolute; top: 1.75em; right: 1em; width: 30em; height: auto; color: #333; background: #ffffcc; border: 1px solid #888; padding: .25em .5em; z-index: 999; border-radius: .2em; box-shadow: #cccccc .2em .2em .2em; } /* Syntax coloring */ .text .com { color: green; font-style: italic; line-height: 1px; } .text .key { font-weight: bold; line-height: 1px; } .text .str { color: #000080; } /* index styles */ #index td, #index th { text-align: right; width: 5em; padding: .25em .5em; border-bottom: 1px solid #eee; } #index th { font-style: italic; color: #333; border-bottom: 1px solid #ccc; cursor: pointer; } #index th:hover { background: #eee; border-bottom: 1px solid #999; } #index td.left, #index th.left { padding-left: 0; } #index td.right, #index th.right { padding-right: 0; } #index th.headerSortDown, #index th.headerSortUp { border-bottom: 1px solid #000; white-space: nowrap; background: #eee; } #index th.headerSortDown:after { content: " ↓"; } #index th.headerSortUp:after { content: " ↑"; } #index td.name, #index th.name { text-align: left; width: auto; } #index td.name a { text-decoration: none; color: #000; } #index tr.total, #index tr.total_dynamic { } #index tr.total td, #index tr.total_dynamic td { font-weight: bold; border-top: 1px solid #ccc; border-bottom: none; } #index tr.file:hover { background: #eeeeee; } #index tr.file:hover td.name { text-decoration: underline; color: #000; } /* scroll marker styles */ #scroll_marker { position: fixed; right: 0; top: 0; width: 16px; height: 100%; background: white; border-left: 1px solid #eee; } #scroll_marker .marker { background: #eedddd; position: absolute; min-height: 3px; width: 100%; } python-coverage-4.5+dfsg.1.orig/coverage/htmlfiles/index.html0000644000076600000620000001014413146037571022244 0ustar staff{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #} {# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt #} {{ title|escape }} {% if extra_css %} {% endif %}
Hide keyboard shortcuts

Hot-keys on this page

n s m x {% if has_arcs %} b p {% endif %} c   change column sorting

{# The title="" attr doesn"t work in Safari. #} {% if has_arcs %} {% endif %} {# HTML syntax requires thead, tfoot, tbody #} {% if has_arcs %} {% endif %} {% for file in files %} {% if has_arcs %} {% endif %} {% endfor %}
Module statements missing excludedbranches partialcoverage
Total {{totals.n_statements}} {{totals.n_missing}} {{totals.n_excluded}}{{totals.n_branches}} {{totals.n_partial_branches}}{{totals.pc_covered_str}}%
{{file.relative_filename}} {{file.nums.n_statements}} {{file.nums.n_missing}} {{file.nums.n_excluded}}{{file.nums.n_branches}} {{file.nums.n_partial_branches}}{{file.nums.pc_covered_str}}%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/coverage/htmlfiles/keybd_closed.png0000755000076600000620000000016013146037571023404 0ustar staffPNG  IHDR# B*17IDATxc  C Ƙ4 $?!dIS]T31 Q)7Ξ"VF"IENDB`python-coverage-4.5+dfsg.1.orig/coverage/htmlfiles/jquery.isonscreen.js0000644000076600000620000000273613146037571024303 0ustar staff/* Copyright (c) 2010 * @author Laurence Wheway * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. * * @version 1.2.0 */ (function($) { jQuery.extend({ isOnScreen: function(box, container) { //ensure numbers come in as intgers (not strings) and remove 'px' is it's there for(var i in box){box[i] = parseFloat(box[i])}; for(var i in container){container[i] = parseFloat(container[i])}; if(!container){ container = { left: $(window).scrollLeft(), top: $(window).scrollTop(), width: $(window).width(), height: $(window).height() } } if( box.left+box.width-container.left > 0 && box.left < container.width+container.left && box.top+box.height-container.top > 0 && box.top < container.height+container.top ) return true; return false; } }) jQuery.fn.isOnScreen = function (container) { for(var i in container){container[i] = parseFloat(container[i])}; if(!container){ container = { left: $(window).scrollLeft(), top: $(window).scrollTop(), width: $(window).width(), height: $(window).height() } } if( $(this).offset().left+$(this).width()-container.left > 0 && $(this).offset().left < container.width+container.left && $(this).offset().top+$(this).height()-container.top > 0 && $(this).offset().top < container.height+container.top ) return true; return false; } })(jQuery); python-coverage-4.5+dfsg.1.orig/coverage/files.py0000644000076600000620000003271713231624345017742 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """File wrangling.""" import hashlib import fnmatch import ntpath import os import os.path import posixpath import re import sys from coverage import env from coverage.backward import unicode_class from coverage.misc import contract, CoverageException, join_regex, isolate_module os = isolate_module(os) def set_relative_directory(): """Set the directory that `relative_filename` will be relative to.""" global RELATIVE_DIR, CANONICAL_FILENAME_CACHE # The absolute path to our current directory. RELATIVE_DIR = os.path.normcase(abs_file(os.curdir) + os.sep) # Cache of results of calling the canonical_filename() method, to # avoid duplicating work. CANONICAL_FILENAME_CACHE = {} def relative_directory(): """Return the directory that `relative_filename` is relative to.""" return RELATIVE_DIR @contract(returns='unicode') def relative_filename(filename): """Return the relative form of `filename`. The file name will be relative to the current directory when the `set_relative_directory` was called. """ fnorm = os.path.normcase(filename) if fnorm.startswith(RELATIVE_DIR): filename = filename[len(RELATIVE_DIR):] return unicode_filename(filename) @contract(returns='unicode') def canonical_filename(filename): """Return a canonical file name for `filename`. An absolute path with no redundant components and normalized case. """ if filename not in CANONICAL_FILENAME_CACHE: if not os.path.isabs(filename): for path in [os.curdir] + sys.path: if path is None: continue f = os.path.join(path, filename) try: exists = os.path.exists(f) except UnicodeError: exists = False if exists: filename = f break cf = abs_file(filename) CANONICAL_FILENAME_CACHE[filename] = cf return CANONICAL_FILENAME_CACHE[filename] MAX_FLAT = 200 @contract(filename='unicode', returns='unicode') def flat_rootname(filename): """A base for a flat file name to correspond to this file. Useful for writing files about the code where you want all the files in the same directory, but need to differentiate same-named files from different directories. For example, the file a/b/c.py will return 'a_b_c_py' """ name = ntpath.splitdrive(filename)[1] name = re.sub(r"[\\/.:]", "_", name) if len(name) > MAX_FLAT: h = hashlib.sha1(name.encode('UTF-8')).hexdigest() name = name[-(MAX_FLAT-len(h)-1):] + '_' + h return name if env.WINDOWS: _ACTUAL_PATH_CACHE = {} _ACTUAL_PATH_LIST_CACHE = {} def actual_path(path): """Get the actual path of `path`, including the correct case.""" if env.PY2 and isinstance(path, unicode_class): path = path.encode(sys.getfilesystemencoding()) if path in _ACTUAL_PATH_CACHE: return _ACTUAL_PATH_CACHE[path] head, tail = os.path.split(path) if not tail: # This means head is the drive spec: normalize it. actpath = head.upper() elif not head: actpath = tail else: head = actual_path(head) if head in _ACTUAL_PATH_LIST_CACHE: files = _ACTUAL_PATH_LIST_CACHE[head] else: try: files = os.listdir(head) except OSError: files = [] _ACTUAL_PATH_LIST_CACHE[head] = files normtail = os.path.normcase(tail) for f in files: if os.path.normcase(f) == normtail: tail = f break actpath = os.path.join(head, tail) _ACTUAL_PATH_CACHE[path] = actpath return actpath else: def actual_path(filename): """The actual path for non-Windows platforms.""" return filename if env.PY2: @contract(returns='unicode') def unicode_filename(filename): """Return a Unicode version of `filename`.""" if isinstance(filename, str): encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() filename = filename.decode(encoding, "replace") return filename else: @contract(filename='unicode', returns='unicode') def unicode_filename(filename): """Return a Unicode version of `filename`.""" return filename @contract(returns='unicode') def abs_file(filename): """Return the absolute normalized form of `filename`.""" path = os.path.expandvars(os.path.expanduser(filename)) try: path = os.path.realpath(path) except UnicodeError: pass path = os.path.abspath(path) path = actual_path(path) path = unicode_filename(path) return path RELATIVE_DIR = None CANONICAL_FILENAME_CACHE = None set_relative_directory() def isabs_anywhere(filename): """Is `filename` an absolute path on any OS?""" return ntpath.isabs(filename) or posixpath.isabs(filename) def prep_patterns(patterns): """Prepare the file patterns for use in a `FnmatchMatcher`. If a pattern starts with a wildcard, it is used as a pattern as-is. If it does not start with a wildcard, then it is made absolute with the current directory. If `patterns` is None, an empty list is returned. """ prepped = [] for p in patterns or []: if p.startswith(("*", "?")): prepped.append(p) else: prepped.append(abs_file(p)) return prepped class TreeMatcher(object): """A matcher for files in a tree. Construct with a list of paths, either files or directories. Paths match with the `match` method if they are one of the files, or if they are somewhere in a subtree rooted at one of the directories. """ def __init__(self, paths): self.paths = list(paths) def __repr__(self): return "" % self.paths def info(self): """A list of strings for displaying when dumping state.""" return self.paths def match(self, fpath): """Does `fpath` indicate a file in one of our trees?""" for p in self.paths: if fpath.startswith(p): if fpath == p: # This is the same file! return True if fpath[len(p)] == os.sep: # This is a file in the directory return True return False class ModuleMatcher(object): """A matcher for modules in a tree.""" def __init__(self, module_names): self.modules = list(module_names) def __repr__(self): return "" % (self.modules) def info(self): """A list of strings for displaying when dumping state.""" return self.modules def match(self, module_name): """Does `module_name` indicate a module in one of our packages?""" if not module_name: return False for m in self.modules: if module_name.startswith(m): if module_name == m: return True if module_name[len(m)] == '.': # This is a module in the package return True return False class FnmatchMatcher(object): """A matcher for files by file name pattern.""" def __init__(self, pats): self.pats = pats[:] # fnmatch is platform-specific. On Windows, it does the Windows thing # of treating / and \ as equivalent. But on other platforms, we need to # take care of that ourselves. fnpats = (fnmatch.translate(p) for p in pats) # Python3.7 fnmatch translates "/" as "/", before that, it translates as "\/", # so we have to deal with maybe a backslash. fnpats = (re.sub(r"\\?/", r"[\\\\/]", p) for p in fnpats) flags = 0 if env.WINDOWS: # Windows is also case-insensitive, so make the regex case-insensitive. flags |= re.IGNORECASE self.re = re.compile(join_regex(fnpats), flags=flags) def __repr__(self): return "" % self.pats def info(self): """A list of strings for displaying when dumping state.""" return self.pats def match(self, fpath): """Does `fpath` match one of our file name patterns?""" return self.re.match(fpath) is not None def sep(s): """Find the path separator used in this string, or os.sep if none.""" sep_match = re.search(r"[\\/]", s) if sep_match: the_sep = sep_match.group(0) else: the_sep = os.sep return the_sep class PathAliases(object): """A collection of aliases for paths. When combining data files from remote machines, often the paths to source code are different, for example, due to OS differences, or because of serialized checkouts on continuous integration machines. A `PathAliases` object tracks a list of pattern/result pairs, and can map a path through those aliases to produce a unified path. """ def __init__(self): self.aliases = [] def pprint(self): # pragma: debugging """Dump the important parts of the PathAliases, for debugging.""" for regex, result in self.aliases: print("{0!r} --> {1!r}".format(regex.pattern, result)) def add(self, pattern, result): """Add the `pattern`/`result` pair to the list of aliases. `pattern` is an `fnmatch`-style pattern. `result` is a simple string. When mapping paths, if a path starts with a match against `pattern`, then that match is replaced with `result`. This models isomorphic source trees being rooted at different places on two different machines. `pattern` can't end with a wildcard component, since that would match an entire tree, and not just its root. """ if len(pattern) > 1: pattern = pattern.rstrip(r"\/") # The pattern can't end with a wildcard component. if pattern.endswith("*"): raise CoverageException("Pattern must not end with wildcards.") pattern_sep = sep(pattern) # The pattern is meant to match a filepath. Let's make it absolute # unless it already is, or is meant to match any prefix. if not pattern.startswith('*') and not isabs_anywhere(pattern): pattern = abs_file(pattern) if not pattern.endswith(pattern_sep): pattern += pattern_sep # Make a regex from the pattern. fnmatch always adds a \Z to # match the whole string, which we don't want, so we remove the \Z. # While removing it, we only replace \Z if followed by paren, or at # end, to keep from destroying a literal \Z in the pattern. regex_pat = fnmatch.translate(pattern) regex_pat = re.sub(r'\\Z(\(|$)', r'\1', regex_pat) # We want */a/b.py to match on Windows too, so change slash to match # either separator. regex_pat = regex_pat.replace(r"\/", r"[\\/]") # We want case-insensitive matching, so add that flag. regex = re.compile(r"(?i)" + regex_pat) # Normalize the result: it must end with a path separator. result_sep = sep(result) result = result.rstrip(r"\/") + result_sep self.aliases.append((regex, result)) def map(self, path): """Map `path` through the aliases. `path` is checked against all of the patterns. The first pattern to match is used to replace the root of the path with the result root. Only one pattern is ever used. If no patterns match, `path` is returned unchanged. The separator style in the result is made to match that of the result in the alias. Returns the mapped path. If a mapping has happened, this is a canonical path. If no mapping has happened, it is the original value of `path` unchanged. """ for regex, result in self.aliases: m = regex.match(path) if m: new = path.replace(m.group(0), result) new = new.replace(sep(path), sep(result)) new = canonical_filename(new) return new return path def find_python_files(dirname): """Yield all of the importable Python files in `dirname`, recursively. To be importable, the files have to be in a directory with a __init__.py, except for `dirname` itself, which isn't required to have one. The assumption is that `dirname` was specified directly, so the user knows best, but sub-directories are checked for a __init__.py to be sure we only find the importable files. """ for i, (dirpath, dirnames, filenames) in enumerate(os.walk(dirname)): if i > 0 and '__init__.py' not in filenames: # If a directory doesn't have __init__.py, then it isn't # importable and neither are its files del dirnames[:] continue for filename in filenames: # We're only interested in files that look like reasonable Python # files: Must end with .py or .pyw, and must not have certain funny # characters that probably mean they are editor junk. if re.match(r"^[^.#~!$@%^&*()+=,]+\.pyw?$", filename): yield os.path.join(dirpath, filename) python-coverage-4.5+dfsg.1.orig/coverage/html.py0000644000076600000620000003560113173515667017612 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """HTML reporting for coverage.py.""" import datetime import json import os import shutil import coverage from coverage import env from coverage.backward import iitems from coverage.files import flat_rootname from coverage.misc import CoverageException, file_be_gone, Hasher, isolate_module from coverage.report import Reporter from coverage.results import Numbers from coverage.templite import Templite os = isolate_module(os) # Static files are looked for in a list of places. STATIC_PATH = [ # The place Debian puts system Javascript libraries. "/usr/share/javascript", # Our htmlfiles directory. os.path.join(os.path.dirname(__file__), "htmlfiles"), ] def data_filename(fname, pkgdir=""): """Return the path to a data file of ours. The file is searched for on `STATIC_PATH`, and the first place it's found, is returned. Each directory in `STATIC_PATH` is searched as-is, and also, if `pkgdir` is provided, at that sub-directory. """ tried = [] for static_dir in STATIC_PATH: static_filename = os.path.join(static_dir, fname) if os.path.exists(static_filename): return static_filename else: tried.append(static_filename) if pkgdir: static_filename = os.path.join(static_dir, pkgdir, fname) if os.path.exists(static_filename): return static_filename else: tried.append(static_filename) raise CoverageException( "Couldn't find static file %r from %r, tried: %r" % (fname, os.getcwd(), tried) ) def read_data(fname): """Return the contents of a data file of ours.""" with open(data_filename(fname)) as data_file: return data_file.read() def write_html(fname, html): """Write `html` to `fname`, properly encoded.""" with open(fname, "wb") as fout: fout.write(html.encode('ascii', 'xmlcharrefreplace')) class HtmlReporter(Reporter): """HTML reporting.""" # These files will be copied from the htmlfiles directory to the output # directory. STATIC_FILES = [ ("style.css", ""), ("jquery.min.js", "jquery"), ("jquery.ba-throttle-debounce.min.js", "jquery-throttle-debounce"), ("jquery.hotkeys.js", "jquery-hotkeys"), ("jquery.isonscreen.js", "jquery-isonscreen"), ("jquery.tablesorter.min.js", "jquery-tablesorter"), ("coverage_html.js", ""), ("keybd_closed.png", ""), ("keybd_open.png", ""), ] def __init__(self, cov, config): super(HtmlReporter, self).__init__(cov, config) self.directory = None title = self.config.html_title if env.PY2: title = title.decode("utf8") self.template_globals = { 'escape': escape, 'pair': pair, 'title': title, '__url__': coverage.__url__, '__version__': coverage.__version__, } self.source_tmpl = Templite(read_data("pyfile.html"), self.template_globals) self.coverage = cov self.files = [] self.all_files_nums = [] self.has_arcs = self.coverage.data.has_arcs() self.status = HtmlStatus() self.extra_css = None self.totals = Numbers() self.time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M') def report(self, morfs): """Generate an HTML report for `morfs`. `morfs` is a list of modules or file names. """ assert self.config.html_dir, "must give a directory for html reporting" # Read the status data. self.status.read(self.config.html_dir) # Check that this run used the same settings as the last run. m = Hasher() m.update(self.config) these_settings = m.hexdigest() if self.status.settings_hash() != these_settings: self.status.reset() self.status.set_settings_hash(these_settings) # The user may have extra CSS they want copied. if self.config.extra_css: self.extra_css = os.path.basename(self.config.extra_css) # Process all the files. self.report_files(self.html_file, morfs, self.config.html_dir) if not self.all_files_nums: raise CoverageException("No data to report.") # Write the index file. self.index_file() self.make_local_static_report_files() return self.totals.n_statements and self.totals.pc_covered def make_local_static_report_files(self): """Make local instances of static files for HTML report.""" # The files we provide must always be copied. for static, pkgdir in self.STATIC_FILES: shutil.copyfile( data_filename(static, pkgdir), os.path.join(self.directory, static) ) # The user may have extra CSS they want copied. if self.extra_css: shutil.copyfile( self.config.extra_css, os.path.join(self.directory, self.extra_css) ) def file_hash(self, source, fr): """Compute a hash that changes if the file needs to be re-reported.""" m = Hasher() m.update(source) self.coverage.data.add_to_hash(fr.filename, m) return m.hexdigest() def html_file(self, fr, analysis): """Generate an HTML file for one source file.""" rootname = flat_rootname(fr.relative_filename()) html_filename = rootname + ".html" html_path = os.path.join(self.directory, html_filename) # Get the numbers for this file. nums = analysis.numbers self.all_files_nums.append(nums) if self.config.skip_covered: # Don't report on 100% files. no_missing_lines = (nums.n_missing == 0) no_missing_branches = (nums.n_partial_branches == 0) if no_missing_lines and no_missing_branches: # If there's an existing file, remove it. file_be_gone(html_path) return source = fr.source() # Find out if the file on disk is already correct. this_hash = self.file_hash(source.encode('utf-8'), fr) that_hash = self.status.file_hash(rootname) if this_hash == that_hash: # Nothing has changed to require the file to be reported again. self.files.append(self.status.index_info(rootname)) return self.status.set_file_hash(rootname, this_hash) if self.has_arcs: missing_branch_arcs = analysis.missing_branch_arcs() arcs_executed = analysis.arcs_executed() # These classes determine which lines are highlighted by default. c_run = "run hide_run" c_exc = "exc" c_mis = "mis" c_par = "par " + c_run lines = [] for lineno, line in enumerate(fr.source_token_lines(), start=1): # Figure out how to mark this line. line_class = [] annotate_html = "" annotate_long = "" if lineno in analysis.statements: line_class.append("stm") if lineno in analysis.excluded: line_class.append(c_exc) elif lineno in analysis.missing: line_class.append(c_mis) elif self.has_arcs and lineno in missing_branch_arcs: line_class.append(c_par) shorts = [] longs = [] for b in missing_branch_arcs[lineno]: if b < 0: shorts.append("exit") else: shorts.append(b) longs.append(fr.missing_arc_description(lineno, b, arcs_executed)) # 202F is NARROW NO-BREAK SPACE. # 219B is RIGHTWARDS ARROW WITH STROKE. short_fmt = "%s ↛ %s" annotate_html = ",   ".join(short_fmt % (lineno, d) for d in shorts) if len(longs) == 1: annotate_long = longs[0] else: annotate_long = "%d missed branches: %s" % ( len(longs), ", ".join("%d) %s" % (num, ann_long) for num, ann_long in enumerate(longs, start=1)), ) elif lineno in analysis.statements: line_class.append(c_run) # Build the HTML for the line. html = [] for tok_type, tok_text in line: if tok_type == "ws": html.append(escape(tok_text)) else: tok_html = escape(tok_text) or ' ' html.append( '%s' % (tok_type, tok_html) ) lines.append({ 'html': ''.join(html), 'number': lineno, 'class': ' '.join(line_class) or "pln", 'annotate': annotate_html, 'annotate_long': annotate_long, }) # Write the HTML page for this file. html = self.source_tmpl.render({ 'c_exc': c_exc, 'c_mis': c_mis, 'c_par': c_par, 'c_run': c_run, 'has_arcs': self.has_arcs, 'extra_css': self.extra_css, 'fr': fr, 'nums': nums, 'lines': lines, 'time_stamp': self.time_stamp, }) write_html(html_path, html) # Save this file's information for the index file. index_info = { 'nums': nums, 'html_filename': html_filename, 'relative_filename': fr.relative_filename(), } self.files.append(index_info) self.status.set_index_info(rootname, index_info) def index_file(self): """Write the index.html file for this report.""" index_tmpl = Templite(read_data("index.html"), self.template_globals) self.totals = sum(self.all_files_nums) html = index_tmpl.render({ 'has_arcs': self.has_arcs, 'extra_css': self.extra_css, 'files': self.files, 'totals': self.totals, 'time_stamp': self.time_stamp, }) write_html(os.path.join(self.directory, "index.html"), html) # Write the latest hashes for next time. self.status.write(self.directory) class HtmlStatus(object): """The status information we keep to support incremental reporting.""" STATUS_FILE = "status.json" STATUS_FORMAT = 1 # pylint: disable=wrong-spelling-in-comment,useless-suppression # The data looks like: # # { # 'format': 1, # 'settings': '540ee119c15d52a68a53fe6f0897346d', # 'version': '4.0a1', # 'files': { # 'cogapp___init__': { # 'hash': 'e45581a5b48f879f301c0f30bf77a50c', # 'index': { # 'html_filename': 'cogapp___init__.html', # 'name': 'cogapp/__init__', # 'nums': , # } # }, # ... # 'cogapp_whiteutils': { # 'hash': '8504bb427fc488c4176809ded0277d51', # 'index': { # 'html_filename': 'cogapp_whiteutils.html', # 'name': 'cogapp/whiteutils', # 'nums': , # } # }, # }, # } def __init__(self): self.reset() def reset(self): """Initialize to empty.""" self.settings = '' self.files = {} def read(self, directory): """Read the last status in `directory`.""" usable = False try: status_file = os.path.join(directory, self.STATUS_FILE) with open(status_file, "r") as fstatus: status = json.load(fstatus) except (IOError, ValueError): usable = False else: usable = True if status['format'] != self.STATUS_FORMAT: usable = False elif status['version'] != coverage.__version__: usable = False if usable: self.files = {} for filename, fileinfo in iitems(status['files']): fileinfo['index']['nums'] = Numbers(*fileinfo['index']['nums']) self.files[filename] = fileinfo self.settings = status['settings'] else: self.reset() def write(self, directory): """Write the current status to `directory`.""" status_file = os.path.join(directory, self.STATUS_FILE) files = {} for filename, fileinfo in iitems(self.files): fileinfo['index']['nums'] = fileinfo['index']['nums'].init_args() files[filename] = fileinfo status = { 'format': self.STATUS_FORMAT, 'version': coverage.__version__, 'settings': self.settings, 'files': files, } with open(status_file, "w") as fout: json.dump(status, fout, separators=(',', ':')) # Older versions of ShiningPanda look for the old name, status.dat. # Accommodate them if we are running under Jenkins. # https://issues.jenkins-ci.org/browse/JENKINS-28428 if "JENKINS_URL" in os.environ: with open(os.path.join(directory, "status.dat"), "w") as dat: dat.write("https://issues.jenkins-ci.org/browse/JENKINS-28428\n") def settings_hash(self): """Get the hash of the coverage.py settings.""" return self.settings def set_settings_hash(self, settings): """Set the hash of the coverage.py settings.""" self.settings = settings def file_hash(self, fname): """Get the hash of `fname`'s contents.""" return self.files.get(fname, {}).get('hash', '') def set_file_hash(self, fname, val): """Set the hash of `fname`'s contents.""" self.files.setdefault(fname, {})['hash'] = val def index_info(self, fname): """Get the information for index.html for `fname`.""" return self.files.get(fname, {}).get('index', {}) def set_index_info(self, fname, info): """Set the information for index.html for `fname`.""" self.files.setdefault(fname, {})['index'] = info # Helpers for templates and generating HTML def escape(t): """HTML-escape the text in `t`. This is only suitable for HTML text, not attributes. """ # Convert HTML special chars into HTML entities. return t.replace("&", "&").replace("<", "<") def pair(ratio): """Format a pair of numbers so JavaScript can read them in an attribute.""" return "%s %s" % ratio python-coverage-4.5+dfsg.1.orig/coverage/backunittest.py0000644000076600000620000000275313146037571021341 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Implementations of unittest features from the future.""" # Use unittest2 if it's available, otherwise unittest. This gives us # back-ported features for 2.6. try: import unittest2 as unittest except ImportError: import unittest def unittest_has(method): """Does `unittest.TestCase` have `method` defined?""" return hasattr(unittest.TestCase, method) class TestCase(unittest.TestCase): """Just like unittest.TestCase, but with assert methods added. Designed to be compatible with 3.1 unittest. Methods are only defined if `unittest` doesn't have them. """ # pylint: disable=missing-docstring # Many Pythons have this method defined. But PyPy3 has a bug with it # somehow (https://bitbucket.org/pypy/pypy/issues/2092), so always use our # own implementation that works everywhere, at least for the ways we're # calling it. def assertCountEqual(self, s1, s2): """Assert these have the same elements, regardless of order.""" self.assertEqual(sorted(s1), sorted(s2)) if not unittest_has('assertRaisesRegex'): def assertRaisesRegex(self, *args, **kwargs): return self.assertRaisesRegexp(*args, **kwargs) if not unittest_has('assertRegex'): def assertRegex(self, *args, **kwargs): return self.assertRegexpMatches(*args, **kwargs) python-coverage-4.5+dfsg.1.orig/coverage/env.py0000644000076600000620000000211713223161333017411 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Determine facts about the environment.""" import os import platform import sys # Operating systems. WINDOWS = sys.platform == "win32" LINUX = sys.platform == "linux2" # Python implementations. PYPY = (platform.python_implementation() == 'PyPy') if PYPY: PYPYVERSION = sys.pypy_version_info JYTHON = (platform.python_implementation() == 'Jython') IRONPYTHON = (platform.python_implementation() == 'IronPython') # Python versions. PYVERSION = sys.version_info PY2 = PYVERSION < (3, 0) PY3 = PYVERSION >= (3, 0) # Coverage.py specifics. # Are we using the C-implemented trace function? C_TRACER = os.getenv('COVERAGE_TEST_TRACER', 'c') == 'c' # Are we coverage-measuring ourselves? METACOV = os.getenv('COVERAGE_COVERAGE', '') != '' # Are we running our test suite? # Even when running tests, you can use COVERAGE_TESTING=0 to disable the # test-specific behavior like contracts. TESTING = os.getenv('COVERAGE_TESTING', '') == 'True' python-coverage-4.5+dfsg.1.orig/coverage/python.py0000644000076600000620000001654613173515667020176 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Python source expertise for coverage.py""" import os.path import types import zipimport from coverage import env, files from coverage.misc import contract, expensive, isolate_module, join_regex from coverage.misc import CoverageException, NoSource from coverage.parser import PythonParser from coverage.phystokens import source_token_lines, source_encoding from coverage.plugin import FileReporter os = isolate_module(os) @contract(returns='bytes') def read_python_source(filename): """Read the Python source text from `filename`. Returns bytes. """ with open(filename, "rb") as f: source = f.read() if env.IRONPYTHON: # IronPython reads Unicode strings even for "rb" files. source = bytes(source) return source.replace(b"\r\n", b"\n").replace(b"\r", b"\n") @contract(returns='unicode') def get_python_source(filename): """Return the source code, as unicode.""" base, ext = os.path.splitext(filename) if ext == ".py" and env.WINDOWS: exts = [".py", ".pyw"] else: exts = [ext] for ext in exts: try_filename = base + ext if os.path.exists(try_filename): # A regular text file: open it. source = read_python_source(try_filename) break # Maybe it's in a zip file? source = get_zip_bytes(try_filename) if source is not None: break else: # Couldn't find source. exc_msg = "No source for code: '%s'.\n" % (filename,) exc_msg += "Aborting report output, consider using -i." raise NoSource(exc_msg) # Replace \f because of http://bugs.python.org/issue19035 source = source.replace(b'\f', b' ') source = source.decode(source_encoding(source), "replace") # Python code should always end with a line with a newline. if source and source[-1] != '\n': source += '\n' return source @contract(returns='bytes|None') def get_zip_bytes(filename): """Get data from `filename` if it is a zip file path. Returns the bytestring data read from the zip file, or None if no zip file could be found or `filename` isn't in it. The data returned will be an empty string if the file is empty. """ markers = ['.zip'+os.sep, '.egg'+os.sep, '.pex'+os.sep] for marker in markers: if marker in filename: parts = filename.split(marker) try: zi = zipimport.zipimporter(parts[0]+marker[:-1]) except zipimport.ZipImportError: continue try: data = zi.get_data(parts[1]) except IOError: continue return data return None def source_for_file(filename): """Return the source file for `filename`. Given a file name being traced, return the best guess as to the source file to attribute it to. """ if filename.endswith(".py"): # .py files are themselves source files. return filename elif filename.endswith((".pyc", ".pyo")): # Bytecode files probably have source files near them. py_filename = filename[:-1] if os.path.exists(py_filename): # Found a .py file, use that. return py_filename if env.WINDOWS: # On Windows, it could be a .pyw file. pyw_filename = py_filename + "w" if os.path.exists(pyw_filename): return pyw_filename # Didn't find source, but it's probably the .py file we want. return py_filename elif filename.endswith("$py.class"): # Jython is easy to guess. return filename[:-9] + ".py" # No idea, just use the file name as-is. return filename class PythonFileReporter(FileReporter): """Report support for a Python file.""" def __init__(self, morf, coverage=None): self.coverage = coverage if hasattr(morf, '__file__'): filename = morf.__file__ elif isinstance(morf, types.ModuleType): # A module should have had .__file__, otherwise we can't use it. # This could be a PEP-420 namespace package. raise CoverageException("Module {0} has no file".format(morf)) else: filename = morf filename = source_for_file(files.unicode_filename(filename)) super(PythonFileReporter, self).__init__(files.canonical_filename(filename)) if hasattr(morf, '__name__'): name = morf.__name__.replace(".", os.sep) if os.path.basename(filename).startswith('__init__.'): name += os.sep + "__init__" name += ".py" name = files.unicode_filename(name) else: name = files.relative_filename(filename) self.relname = name self._source = None self._parser = None self._statements = None self._excluded = None def __repr__(self): return "".format(self.filename) @contract(returns='unicode') def relative_filename(self): return self.relname @property def parser(self): """Lazily create a :class:`PythonParser`.""" if self._parser is None: self._parser = PythonParser( filename=self.filename, exclude=self.coverage._exclude_regex('exclude'), ) self._parser.parse_source() return self._parser def lines(self): """Return the line numbers of statements in the file.""" return self.parser.statements def excluded_lines(self): """Return the line numbers of statements in the file.""" return self.parser.excluded def translate_lines(self, lines): return self.parser.translate_lines(lines) def translate_arcs(self, arcs): return self.parser.translate_arcs(arcs) @expensive def no_branch_lines(self): no_branch = self.parser.lines_matching( join_regex(self.coverage.config.partial_list), join_regex(self.coverage.config.partial_always_list) ) return no_branch @expensive def arcs(self): return self.parser.arcs() @expensive def exit_counts(self): return self.parser.exit_counts() def missing_arc_description(self, start, end, executed_arcs=None): return self.parser.missing_arc_description(start, end, executed_arcs) @contract(returns='unicode') def source(self): if self._source is None: self._source = get_python_source(self.filename) return self._source def should_be_python(self): """Does it seem like this file should contain Python? This is used to decide if a file reported as part of the execution of a program was really likely to have contained Python in the first place. """ # Get the file extension. _, ext = os.path.splitext(self.filename) # Anything named *.py* should be Python. if ext.startswith('.py'): return True # A file with no extension should be Python. if not ext: return True # Everything else is probably not Python. return False def source_token_lines(self): return source_token_lines(self.source()) python-coverage-4.5+dfsg.1.orig/coverage/plugin.py0000644000076600000620000003760013224227025020126 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """ Plug-in interfaces for coverage.py. Coverage.py supports a few different kinds of plug-ins that change its behavior: * File tracers implement tracing of non-Python file types. * Configurers add custom configuration, using Python code to change the configuration. To write a coverage.py plug-in, create a module with a subclass of :class:`~coverage.CoveragePlugin`. You will override methods in your class to participate in various aspects of coverage.py's processing. Different types of plug-ins have to override different methods. Any plug-in can optionally implement :meth:`~coverage.CoveragePlugin.sys_info` to provide debugging information about their operation. Your module must also contain a ``coverage_init`` function that registers an instance of your plug-in class:: import coverage class MyPlugin(coverage.CoveragePlugin): ... def coverage_init(reg, options): reg.add_file_tracer(MyPlugin()) You use the `reg` parameter passed to your ``coverage_init`` function to register your plug-in object. The registration method you call depends on what kind of plug-in it is. If your plug-in takes options, the `options` parameter is a dictionary of your plug-in's options from the coverage.py configuration file. Use them however you want to configure your object before registering it. Coverage.py will store its own information on your plug-in object, using attributes whose names start with ``_coverage_``. Don't be startled. File Tracers ============ File tracers implement measurement support for non-Python files. File tracers implement the :meth:`~coverage.CoveragePlugin.file_tracer` method to claim files and the :meth:`~coverage.CoveragePlugin.file_reporter` method to report on those files. In your ``coverage_init`` function, use the ``add_file_tracer`` method to register your file tracer. Configurers =========== Configurers modify the configuration of coverage.py during start-up. Configurers implement the :meth:`~coverage.CoveragePlugin.configure` method to change the configuration. In your ``coverage_init`` function, use the ``add_configurer`` method to register your configurer. """ from coverage import files from coverage.misc import contract, _needs_to_implement class CoveragePlugin(object): """Base class for coverage.py plug-ins.""" def file_tracer(self, filename): # pylint: disable=unused-argument """Get a :class:`FileTracer` object for a file. Plug-in type: file tracer. Every Python source file is offered to your plug-in to give it a chance to take responsibility for tracing the file. If your plug-in can handle the file, then return a :class:`FileTracer` object. Otherwise return None. There is no way to register your plug-in for particular files. Instead, this method is invoked for all files, and the plug-in decides whether it can trace the file or not. Be prepared for `filename` to refer to all kinds of files that have nothing to do with your plug-in. The file name will be a Python file being executed. There are two broad categories of behavior for a plug-in, depending on the kind of files your plug-in supports: * Static file names: each of your original source files has been converted into a distinct Python file. Your plug-in is invoked with the Python file name, and it maps it back to its original source file. * Dynamic file names: all of your source files are executed by the same Python file. In this case, your plug-in implements :meth:`FileTracer.dynamic_source_filename` to provide the actual source file for each execution frame. `filename` is a string, the path to the file being considered. This is the absolute real path to the file. If you are comparing to other paths, be sure to take this into account. Returns a :class:`FileTracer` object to use to trace `filename`, or None if this plug-in cannot trace this file. """ return None def file_reporter(self, filename): # pylint: disable=unused-argument """Get the :class:`FileReporter` class to use for a file. Plug-in type: file tracer. This will only be invoked if `filename` returns non-None from :meth:`file_tracer`. It's an error to return None from this method. Returns a :class:`FileReporter` object to use to report on `filename`. """ _needs_to_implement(self, "file_reporter") def find_executable_files(self, src_dir): # pylint: disable=unused-argument """Yield all of the executable files in `src_dir`, recursively. Plug-in type: file tracer. Executability is a plug-in-specific property, but generally means files which would have been considered for coverage analysis, had they been included automatically. Returns or yields a sequence of strings, the paths to files that could have been executed, including files that had been executed. """ return [] def configure(self, config): """Modify the configuration of coverage.py. Plug-in type: configurer. This method is called during coverage.py start-up, to give your plug-in a change to change the configuration. The `config` parameter is an object with :meth:`~coverage.Coverage.get_option` and :meth:`~coverage.Coverage.set_option` methods. Do not call any other methods on the `config` object. """ pass def sys_info(self): """Get a list of information useful for debugging. Plug-in type: any. This method will be invoked for ``--debug=sys``. Your plug-in can return any information it wants to be displayed. Returns a list of pairs: `[(name, value), ...]`. """ return [] class FileTracer(object): """Support needed for files during the execution phase. File tracer plug-ins implement subclasses of FileTracer to return from their :meth:`~CoveragePlugin.file_tracer` method. You may construct this object from :meth:`CoveragePlugin.file_tracer` any way you like. A natural choice would be to pass the file name given to `file_tracer`. `FileTracer` objects should only be created in the :meth:`CoveragePlugin.file_tracer` method. See :ref:`howitworks` for details of the different coverage.py phases. """ def source_filename(self): """The source file name for this file. This may be any file name you like. A key responsibility of a plug-in is to own the mapping from Python execution back to whatever source file name was originally the source of the code. See :meth:`CoveragePlugin.file_tracer` for details about static and dynamic file names. Returns the file name to credit with this execution. """ _needs_to_implement(self, "source_filename") def has_dynamic_source_filename(self): """Does this FileTracer have dynamic source file names? FileTracers can provide dynamically determined file names by implementing :meth:`dynamic_source_filename`. Invoking that function is expensive. To determine whether to invoke it, coverage.py uses the result of this function to know if it needs to bother invoking :meth:`dynamic_source_filename`. See :meth:`CoveragePlugin.file_tracer` for details about static and dynamic file names. Returns True if :meth:`dynamic_source_filename` should be called to get dynamic source file names. """ return False def dynamic_source_filename(self, filename, frame): # pylint: disable=unused-argument """Get a dynamically computed source file name. Some plug-ins need to compute the source file name dynamically for each frame. This function will not be invoked if :meth:`has_dynamic_source_filename` returns False. Returns the source file name for this frame, or None if this frame shouldn't be measured. """ return None def line_number_range(self, frame): """Get the range of source line numbers for a given a call frame. The call frame is examined, and the source line number in the original file is returned. The return value is a pair of numbers, the starting line number and the ending line number, both inclusive. For example, returning (5, 7) means that lines 5, 6, and 7 should be considered executed. This function might decide that the frame doesn't indicate any lines from the source file were executed. Return (-1, -1) in this case to tell coverage.py that no lines should be recorded for this frame. """ lineno = frame.f_lineno return lineno, lineno class FileReporter(object): """Support needed for files during the analysis and reporting phases. File tracer plug-ins implement a subclass of `FileReporter`, and return instances from their :meth:`CoveragePlugin.file_reporter` method. There are many methods here, but only :meth:`lines` is required, to provide the set of executable lines in the file. See :ref:`howitworks` for details of the different coverage.py phases. """ def __init__(self, filename): """Simple initialization of a `FileReporter`. The `filename` argument is the path to the file being reported. This will be available as the `.filename` attribute on the object. Other method implementations on this base class rely on this attribute. """ self.filename = filename def __repr__(self): return "<{0.__class__.__name__} filename={0.filename!r}>".format(self) def relative_filename(self): """Get the relative file name for this file. This file path will be displayed in reports. The default implementation will supply the actual project-relative file path. You only need to supply this method if you have an unusual syntax for file paths. """ return files.relative_filename(self.filename) @contract(returns='unicode') def source(self): """Get the source for the file. Returns a Unicode string. The base implementation simply reads the `self.filename` file and decodes it as UTF8. Override this method if your file isn't readable as a text file, or if you need other encoding support. """ with open(self.filename, "rb") as f: return f.read().decode("utf8") def lines(self): """Get the executable lines in this file. Your plug-in must determine which lines in the file were possibly executable. This method returns a set of those line numbers. Returns a set of line numbers. """ _needs_to_implement(self, "lines") def excluded_lines(self): """Get the excluded executable lines in this file. Your plug-in can use any method it likes to allow the user to exclude executable lines from consideration. Returns a set of line numbers. The base implementation returns the empty set. """ return set() def translate_lines(self, lines): """Translate recorded lines into reported lines. Some file formats will want to report lines slightly differently than they are recorded. For example, Python records the last line of a multi-line statement, but reports are nicer if they mention the first line. Your plug-in can optionally define this method to perform these kinds of adjustment. `lines` is a sequence of integers, the recorded line numbers. Returns a set of integers, the adjusted line numbers. The base implementation returns the numbers unchanged. """ return set(lines) def arcs(self): """Get the executable arcs in this file. To support branch coverage, your plug-in needs to be able to indicate possible execution paths, as a set of line number pairs. Each pair is a `(prev, next)` pair indicating that execution can transition from the `prev` line number to the `next` line number. Returns a set of pairs of line numbers. The default implementation returns an empty set. """ return set() def no_branch_lines(self): """Get the lines excused from branch coverage in this file. Your plug-in can use any method it likes to allow the user to exclude lines from consideration of branch coverage. Returns a set of line numbers. The base implementation returns the empty set. """ return set() def translate_arcs(self, arcs): """Translate recorded arcs into reported arcs. Similar to :meth:`translate_lines`, but for arcs. `arcs` is a set of line number pairs. Returns a set of line number pairs. The default implementation returns `arcs` unchanged. """ return arcs def exit_counts(self): """Get a count of exits from that each line. To determine which lines are branches, coverage.py looks for lines that have more than one exit. This function creates a dict mapping each executable line number to a count of how many exits it has. To be honest, this feels wrong, and should be refactored. Let me know if you attempt to implement this method in your plug-in... """ return {} def missing_arc_description(self, start, end, executed_arcs=None): # pylint: disable=unused-argument """Provide an English sentence describing a missing arc. The `start` and `end` arguments are the line numbers of the missing arc. Negative numbers indicate entering or exiting code objects. The `executed_arcs` argument is a set of line number pairs, the arcs that were executed in this file. By default, this simply returns the string "Line {start} didn't jump to {end}". """ return "Line {start} didn't jump to line {end}".format(start=start, end=end) def source_token_lines(self): """Generate a series of tokenized lines, one for each line in `source`. These tokens are used for syntax-colored reports. Each line is a list of pairs, each pair is a token:: [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ] Each pair has a token class, and the token text. The token classes are: * ``'com'``: a comment * ``'key'``: a keyword * ``'nam'``: a name, or identifier * ``'num'``: a number * ``'op'``: an operator * ``'str'``: a string literal * ``'txt'``: some other kind of text If you concatenate all the token texts, and then join them with newlines, you should have your original source back. The default implementation simply returns each line tagged as ``'txt'``. """ for line in self.source().splitlines(): yield [('txt', line)] # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all # of them defined. def __eq__(self, other): return isinstance(other, FileReporter) and self.filename == other.filename def __ne__(self, other): return not (self == other) def __lt__(self, other): return self.filename < other.filename def __le__(self, other): return self.filename <= other.filename def __gt__(self, other): return self.filename > other.filename def __ge__(self, other): return self.filename >= other.filename __hash__ = None # This object doesn't need to be hashed. python-coverage-4.5+dfsg.1.orig/igor.py0000644000076600000620000002742513223161346016003 0ustar staff# coding: utf-8 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Helper for building, testing, and linting coverage.py. To get portability, all these operations are written in Python here instead of in shell scripts, batch files, or Makefiles. """ import contextlib import fnmatch import glob import inspect import os import platform import sys import textwrap import warnings import zipfile import pytest @contextlib.contextmanager def ignore_warnings(): """Context manager to ignore warning within the with statement.""" with warnings.catch_warnings(): warnings.simplefilter("ignore") yield # Functions named do_* are executable from the command line: do_blah is run # by "python igor.py blah". def do_show_env(): """Show the environment variables.""" print("Environment:") for env in sorted(os.environ): print(" %s = %r" % (env, os.environ[env])) def do_remove_extension(): """Remove the compiled C extension, no matter what its name.""" so_patterns = """ tracer.so tracer.*.so tracer.pyd tracer.*.pyd """.split() for pattern in so_patterns: pattern = os.path.join("coverage", pattern) for filename in glob.glob(pattern): try: os.remove(filename) except OSError: pass def label_for_tracer(tracer): """Get the label for these tests.""" if tracer == "py": label = "with Python tracer" else: label = "with C tracer" return label def should_skip(tracer): """Is there a reason to skip these tests?""" if tracer == "py": skipper = os.environ.get("COVERAGE_NO_PYTRACER") else: skipper = os.environ.get("COVERAGE_NO_CTRACER") if skipper: msg = "Skipping tests " + label_for_tracer(tracer) if len(skipper) > 1: msg += ": " + skipper else: msg = "" return msg def run_tests(tracer, *runner_args): """The actual running of tests.""" if 'COVERAGE_TESTING' not in os.environ: os.environ['COVERAGE_TESTING'] = "True" print_banner(label_for_tracer(tracer)) return pytest.main(list(runner_args)) def run_tests_with_coverage(tracer, *runner_args): """Run tests, but with coverage.""" # Need to define this early enough that the first import of env.py sees it. os.environ['COVERAGE_TESTING'] = "True" os.environ['COVERAGE_PROCESS_START'] = os.path.abspath('metacov.ini') os.environ['COVERAGE_HOME'] = os.getcwd() # Create the .pth file that will let us measure coverage in sub-processes. # The .pth file seems to have to be alphabetically after easy-install.pth # or the sys.path entries aren't created right? pth_dir = os.path.dirname(pytest.__file__) pth_path = os.path.join(pth_dir, "zzz_metacov.pth") with open(pth_path, "w") as pth_file: pth_file.write("import coverage; coverage.process_startup()\n") # Make names for the data files that keep all the test runs distinct. impl = platform.python_implementation().lower() version = "%s%s" % sys.version_info[:2] if '__pypy__' in sys.builtin_module_names: version += "_%s%s" % sys.pypy_version_info[:2] suffix = "%s%s_%s_%s" % (impl, version, tracer, platform.platform()) os.environ['COVERAGE_METAFILE'] = os.path.abspath(".metacov."+suffix) import coverage cov = coverage.Coverage(config_file="metacov.ini", data_suffix=False) # Cheap trick: the coverage.py code itself is excluded from measurement, # but if we clobber the cover_prefix in the coverage object, we can defeat # the self-detection. cov.cover_prefix = "Please measure coverage.py!" cov._warn_unimported_source = False cov.start() try: # Re-import coverage to get it coverage tested! I don't understand all # the mechanics here, but if I don't carry over the imported modules # (in covmods), then things go haywire (os == None, eventually). covmods = {} covdir = os.path.split(coverage.__file__)[0] # We have to make a list since we'll be deleting in the loop. modules = list(sys.modules.items()) for name, mod in modules: if name.startswith('coverage'): if getattr(mod, '__file__', "??").startswith(covdir): covmods[name] = mod del sys.modules[name] import coverage # pylint: disable=reimported sys.modules.update(covmods) # Run tests, with the arguments from our command line. status = run_tests(tracer, *runner_args) finally: cov.stop() os.remove(pth_path) cov.combine() cov.save() return status def do_combine_html(): """Combine data from a meta-coverage run, and make the HTML and XML reports.""" import coverage os.environ['COVERAGE_HOME'] = os.getcwd() os.environ['COVERAGE_METAFILE'] = os.path.abspath(".metacov") cov = coverage.Coverage(config_file="metacov.ini") cov.load() cov.combine() cov.save() cov.html_report() cov.xml_report() def do_test_with_tracer(tracer, *runner_args): """Run tests with a particular tracer.""" # If we should skip these tests, skip them. skip_msg = should_skip(tracer) if skip_msg: print(skip_msg) return None os.environ["COVERAGE_TEST_TRACER"] = tracer if os.environ.get("COVERAGE_COVERAGE", ""): return run_tests_with_coverage(tracer, *runner_args) else: return run_tests(tracer, *runner_args) def do_zip_mods(): """Build the zipmods.zip file.""" zf = zipfile.ZipFile("tests/zipmods.zip", "w") # Take one file from disk. zf.write("tests/covmodzip1.py", "covmodzip1.py") # The others will be various encodings. source = textwrap.dedent(u"""\ # coding: {encoding} text = u"{text}" ords = {ords} assert [ord(c) for c in text] == ords print(u"All OK with {encoding}") """) # These encodings should match the list in tests/test_python.py details = [ (u'utf8', u'ⓗⓔⓛⓛⓞ, ⓦⓞⓡⓛⓓ'), (u'gb2312', u'你好,世界'), (u'hebrew', u'שלום, עולם'), (u'shift_jis', u'こんにちは世界'), (u'cp1252', u'“hi”'), ] for encoding, text in details: filename = 'encoded_{0}.py'.format(encoding) ords = [ord(c) for c in text] source_text = source.format(encoding=encoding, text=text, ords=ords) zf.writestr(filename, source_text.encode(encoding)) zf.close() def do_install_egg(): """Install the egg1 egg for tests.""" # I am pretty certain there are easier ways to install eggs... # pylint: disable=import-error,no-name-in-module cur_dir = os.getcwd() os.chdir("tests/eggsrc") with ignore_warnings(): import distutils.core distutils.core.run_setup("setup.py", ["--quiet", "bdist_egg"]) egg = glob.glob("dist/*.egg")[0] distutils.core.run_setup( "setup.py", ["--quiet", "easy_install", "--no-deps", "--zip-ok", egg] ) os.chdir(cur_dir) def do_check_eol(): """Check files for incorrect newlines and trailing whitespace.""" ignore_dirs = [ '.svn', '.hg', '.git', '.tox*', '*.egg-info', '_build', ] checked = set() def check_file(fname, crlf=True, trail_white=True): """Check a single file for whitespace abuse.""" fname = os.path.relpath(fname) if fname in checked: return checked.add(fname) line = None with open(fname, "rb") as f: for n, line in enumerate(f, start=1): if crlf: if b"\r" in line: print("%s@%d: CR found" % (fname, n)) return if trail_white: line = line[:-1] if not crlf: line = line.rstrip(b'\r') if line.rstrip() != line: print("%s@%d: trailing whitespace found" % (fname, n)) return if line is not None and not line.strip(): print("%s: final blank line" % (fname,)) def check_files(root, patterns, **kwargs): """Check a number of files for whitespace abuse.""" for where, dirs, files in os.walk(root): for f in files: fname = os.path.join(where, f) for p in patterns: if fnmatch.fnmatch(fname, p): check_file(fname, **kwargs) break for ignore_dir in ignore_dirs: ignored = [] for dir_name in dirs: if fnmatch.fnmatch(dir_name, ignore_dir): ignored.append(dir_name) for dir_name in ignored: dirs.remove(dir_name) check_files("coverage", ["*.py"]) check_files("coverage/ctracer", ["*.c", "*.h"]) check_files("coverage/htmlfiles", ["*.html", "*.css", "*.js"]) check_file("tests/farm/html/src/bom.py", crlf=False) check_files("tests", ["*.py"]) check_files("tests", ["*,cover"], trail_white=False) check_files("tests/js", ["*.js", "*.html"]) check_file("setup.py") check_file("igor.py") check_file("Makefile") check_file(".hgignore") check_file(".travis.yml") check_files(".", ["*.rst", "*.txt"]) check_files(".", ["*.pip"]) def print_banner(label): """Print the version of Python.""" try: impl = platform.python_implementation() except AttributeError: impl = "Python" version = platform.python_version() if '__pypy__' in sys.builtin_module_names: version += " (pypy %s)" % ".".join(str(v) for v in sys.pypy_version_info) try: which_python = os.path.relpath(sys.executable) except ValueError: # On Windows having a python executable on a different drive # than the sources cannot be relative. which_python = sys.executable print('=== %s %s %s (%s) ===' % (impl, version, label, which_python)) sys.stdout.flush() def do_help(): """List the available commands""" items = list(globals().items()) items.sort() for name, value in items: if name.startswith('do_'): print("%-20s%s" % (name[3:], value.__doc__)) def analyze_args(function): """What kind of args does `function` expect? Returns: star, num_pos: star(boolean): Does `function` accept *args? num_args(int): How many positional arguments does `function` have? """ try: getargspec = inspect.getfullargspec except AttributeError: getargspec = inspect.getargspec argspec = getargspec(function) return bool(argspec[1]), len(argspec[0]) def main(args): """Main command-line execution for igor. Verbs are taken from the command line, and extra words taken as directed by the arguments needed by the handler. """ while args: verb = args.pop(0) handler = globals().get('do_'+verb) if handler is None: print("*** No handler for %r" % verb) return 1 star, num_args = analyze_args(handler) if star: # Handler has *args, give it all the rest of the command line. handler_args = args args = [] else: # Handler has specific arguments, give it only what it needs. handler_args = args[:num_args] args = args[num_args:] ret = handler(*handler_args) # If a handler returns a failure-like value, stop. if ret: return ret return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) python-coverage-4.5+dfsg.1.orig/tests/0000755000076600000620000000000013235414515015623 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/test_oddball.py0000644000076600000620000005175113177624110020645 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Oddball cases for testing coverage.py""" import sys from flaky import flaky import pytest import coverage from coverage import env from coverage.backward import import_local_file from coverage.files import abs_file from tests.coveragetest import CoverageTest from tests import osinfo class ThreadingTest(CoverageTest): """Tests of the threading support.""" def test_threading(self): self.check_coverage("""\ import threading def fromMainThread(): return "called from main thread" def fromOtherThread(): return "called from other thread" def neverCalled(): return "no one calls me" other = threading.Thread(target=fromOtherThread) other.start() fromMainThread() other.join() """, [1, 3, 4, 6, 7, 9, 10, 12, 13, 14, 15], "10") def test_thread_run(self): self.check_coverage("""\ import threading class TestThread(threading.Thread): def run(self): self.a = 5 self.do_work() self.a = 7 def do_work(self): self.a = 10 thd = TestThread() thd.start() thd.join() """, [1, 3, 4, 5, 6, 7, 9, 10, 12, 13, 14], "") class RecursionTest(CoverageTest): """Check what happens when recursive code gets near limits.""" def test_short_recursion(self): # We can definitely get close to 500 stack frames. self.check_coverage("""\ def recur(n): if n == 0: return 0 else: return recur(n-1)+1 recur(495) # We can get at least this many stack frames. i = 8 # and this line will be traced """, [1, 2, 3, 5, 7, 8], "") def test_long_recursion(self): # We can't finish a very deep recursion, but we don't crash. with self.assertRaises(RuntimeError): self.check_coverage("""\ def recur(n): if n == 0: return 0 else: return recur(n-1)+1 recur(100000) # This is definitely too many frames. """, [1, 2, 3, 5, 7], "" ) def test_long_recursion_recovery(self): # Test the core of bug 93: https://bitbucket.org/ned/coveragepy/issue/93 # When recovering from a stack overflow, the Python trace function is # disabled, but the C trace function is not. So if we're using a # Python trace function, we won't trace anything after the stack # overflow, and there should be a warning about it. If we're using # the C trace function, only line 3 will be missing, and all else # will be traced. self.make_file("recur.py", """\ def recur(n): if n == 0: return 0 # never hit else: return recur(n-1)+1 try: recur(100000) # This is definitely too many frames. except RuntimeError: i = 10 i = 11 """) cov = coverage.Coverage() self.start_import_stop(cov, "recur") pytrace = (cov.collector.tracer_name() == "PyTracer") expected_missing = [3] if pytrace: # pragma: no metacov expected_missing += [9, 10, 11] _, statements, missing, _ = cov.analysis("recur.py") self.assertEqual(statements, [1, 2, 3, 5, 7, 8, 9, 10, 11]) self.assertEqual(missing, expected_missing) # Get a warning about the stackoverflow effect on the tracing function. if pytrace: # pragma: no metacov self.assertEqual(cov._warnings, ["Trace function changed, measurement is likely wrong: None"] ) else: self.assertEqual(cov._warnings, []) class MemoryLeakTest(CoverageTest): """Attempt the impossible: test that memory doesn't leak. Note: this test is truly unusual, and has had a colorful history. See for example: https://bitbucket.org/ned/coveragepy/issue/186 It may still fail occasionally, especially on PyPy. """ @flaky def test_for_leaks(self): if env.JYTHON: self.skipTest("Don't bother on Jython") # Our original bad memory leak only happened on line numbers > 255, so # make a code object with more lines than that. Ugly string mumbo # jumbo to get 300 blank lines at the beginning.. code = """\ # blank line\n""" * 300 + """\ def once(x): # line 301 if x % 100 == 0: raise Exception("100!") elif x % 2: return 10 else: # line 306 return 11 i = 0 # Portable loop without alloc'ing memory. while i < ITERS: try: once(i) except: pass i += 1 # line 315 """ lines = list(range(301, 315)) lines.remove(306) # Line 306 is the "else". # This is a non-deterministic test, so try it a few times, and fail it # only if it predominantly fails. fails = 0 for _ in range(10): ram_0 = osinfo.process_ram() self.check_coverage(code.replace("ITERS", "10"), lines, "") ram_10 = osinfo.process_ram() self.check_coverage(code.replace("ITERS", "10000"), lines, "") ram_10k = osinfo.process_ram() # Running the code 10k times shouldn't grow the ram much more than # running it 10 times. ram_growth = (ram_10k - ram_10) - (ram_10 - ram_0) if ram_growth > 100000: fails += 1 # pragma: only failure if fails > 8: self.fail("RAM grew by %d" % (ram_growth)) # pragma: only failure class MemoryFumblingTest(CoverageTest): """Test that we properly manage the None refcount.""" def test_dropping_none(self): # pragma: not covered if not env.C_TRACER: self.skipTest("Only the C tracer has refcounting issues") # TODO: Mark this so it will only be run sometimes. self.skipTest("This is too expensive for now (30s)") # Start and stop coverage thousands of times to flush out bad # reference counting, maybe. self.make_file("the_code.py", """\ import random def f(): if random.random() > .5: x = 1 else: x = 2 """) self.make_file("main.py", """\ import coverage import sys from the_code import f for i in range(10000): cov = coverage.Coverage(branch=True) cov.start() f() cov.stop() cov.erase() print("Final None refcount: %d" % (sys.getrefcount(None))) """) status, out = self.run_command_status("python main.py") self.assertEqual(status, 0) self.assertIn("Final None refcount", out) self.assertNotIn("Fatal", out) class PyexpatTest(CoverageTest): """Pyexpat screws up tracing. Make sure we've counter-defended properly.""" def test_pyexpat(self): if env.JYTHON: self.skipTest("Pyexpat isn't a problem on Jython") # pyexpat calls the trace function explicitly (inexplicably), and does # it wrong for exceptions. Parsing a DOCTYPE for some reason throws # an exception internally, and triggers its wrong behavior. This test # checks that our fake PyTrace_RETURN hack in tracer.c works. It will # also detect if the pyexpat bug is fixed unbeknownst to us, meaning # we'd see two RETURNs where there should only be one. self.make_file("trydom.py", """\ import xml.dom.minidom XML = '''\\ ''' def foo(): dom = xml.dom.minidom.parseString(XML) assert len(dom.getElementsByTagName('child')) == 2 a = 11 foo() """) self.make_file("outer.py", "\n"*100 + "import trydom\na = 102\n") cov = coverage.Coverage() cov.erase() # Import the Python file, executing it. self.start_import_stop(cov, "outer") _, statements, missing, _ = cov.analysis("trydom.py") self.assertEqual(statements, [1, 3, 8, 9, 10, 11, 13]) self.assertEqual(missing, []) _, statements, missing, _ = cov.analysis("outer.py") self.assertEqual(statements, [101, 102]) self.assertEqual(missing, []) # Make sure pyexpat isn't recorded as a source file. # https://bitbucket.org/ned/coveragepy/issues/419/nosource-no-source-for-code-path-to-c files = cov.get_data().measured_files() self.assertFalse( any(f.endswith("pyexpat.c") for f in files), "Pyexpat.c is in the measured files!: %r:" % (files,) ) class ExceptionTest(CoverageTest): """I suspect different versions of Python deal with exceptions differently in the trace function. """ def test_exception(self): # Python 2.3's trace function doesn't get called with "return" if the # scope is exiting due to an exception. This confounds our trace # function which relies on scope announcements to track which files to # trace. # # This test is designed to sniff this out. Each function in the call # stack is in a different file, to try to trip up the tracer. Each # file has active lines in a different range so we'll see if the lines # get attributed to the wrong file. self.make_file("oops.py", """\ def oops(args): a = 2 raise Exception("oops") a = 4 """) self.make_file("fly.py", "\n"*100 + """\ def fly(calls): a = 2 calls[0](calls[1:]) a = 4 """) self.make_file("catch.py", "\n"*200 + """\ def catch(calls): try: a = 3 calls[0](calls[1:]) a = 5 except: a = 7 """) self.make_file("doit.py", "\n"*300 + """\ def doit(calls): try: calls[0](calls[1:]) except: a = 5 """) # Import all the modules before starting coverage, so the def lines # won't be in all the results. for mod in "oops fly catch doit".split(): import_local_file(mod) # Each run nests the functions differently to get different # combinations of catching exceptions and letting them fly. runs = [ ("doit fly oops", { 'doit.py': [302, 303, 304, 305], 'fly.py': [102, 103], 'oops.py': [2, 3], }), ("doit catch oops", { 'doit.py': [302, 303], 'catch.py': [202, 203, 204, 206, 207], 'oops.py': [2, 3], }), ("doit fly catch oops", { 'doit.py': [302, 303], 'fly.py': [102, 103, 104], 'catch.py': [202, 203, 204, 206, 207], 'oops.py': [2, 3], }), ("doit catch fly oops", { 'doit.py': [302, 303], 'catch.py': [202, 203, 204, 206, 207], 'fly.py': [102, 103], 'oops.py': [2, 3], }), ] for callnames, lines_expected in runs: # Make the list of functions we'll call for this test. callnames = callnames.split() calls = [getattr(sys.modules[cn], cn) for cn in callnames] cov = coverage.Coverage() cov.start() # Call our list of functions: invoke the first, with the rest as # an argument. calls[0](calls[1:]) # pragma: nested cov.stop() # pragma: nested # Clean the line data and compare to expected results. # The file names are absolute, so keep just the base. clean_lines = {} data = cov.get_data() for callname in callnames: filename = callname + ".py" lines = data.lines(abs_file(filename)) clean_lines[filename] = sorted(lines) if env.JYTHON: # pragma: only jython # Jython doesn't report on try or except lines, so take those # out of the expected lines. invisible = [202, 206, 302, 304] for lines in lines_expected.values(): lines[:] = [l for l in lines if l not in invisible] self.assertEqual(clean_lines, lines_expected) class DoctestTest(CoverageTest): """Tests invoked with doctest should measure properly.""" def setUp(self): super(DoctestTest, self).setUp() # This test case exists because Python 2.4's doctest module didn't play # well with coverage. Nose fixes the problem by monkeypatching doctest. # I want to be sure there's no monkeypatch and that I'm getting the # doctest module that users of coverage will get. assert 'doctest' not in sys.modules def test_doctest(self): self.check_coverage('''\ def return_arg_or_void(arg): """If is None, return "Void"; otherwise return >>> return_arg_or_void(None) 'Void' >>> return_arg_or_void("arg") 'arg' >>> return_arg_or_void("None") 'None' """ if arg is None: return "Void" else: return arg import doctest, sys doctest.testmod(sys.modules[__name__]) # we're not __main__ :( ''', [1, 11, 12, 14, 16, 17], "") class GettraceTest(CoverageTest): """Tests that we work properly with `sys.gettrace()`.""" def test_round_trip_in_untraced_function(self): # https://bitbucket.org/ned/coveragepy/issues/575/running-doctest-prevents-complete-coverage self.make_file("main.py", """import sample""") self.make_file("sample.py", """\ from swap import swap_it def doit(): print(3) swap_it() print(5) def doit_soon(): print(7) doit() print(9) print(10) doit_soon() print(12) """) self.make_file("swap.py", """\ import sys def swap_it(): sys.settrace(sys.gettrace()) """) # Use --source=sample to prevent measurement of swap.py. cov = coverage.Coverage(source=["sample"]) self.start_import_stop(cov, "main") self.assertEqual(self.stdout(), "10\n7\n3\n5\n9\n12\n") _, statements, missing, _ = cov.analysis("sample.py") self.assertEqual(statements, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) self.assertEqual(missing, []) def test_setting_new_trace_function(self): # https://bitbucket.org/ned/coveragepy/issues/436/disabled-coverage-ctracer-may-rise-from self.check_coverage('''\ import sys def tracer(frame, event, arg): print("%s: %s @ %d" % (event, frame.f_code.co_filename, frame.f_lineno)) return tracer def begin(): sys.settrace(tracer) def collect(): t = sys.gettrace() assert t is tracer, t def test_unsets_trace(): begin() collect() old = sys.gettrace() test_unsets_trace() sys.settrace(old) a = 21 b = 22 ''', lines=[1, 3, 4, 5, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20, 21, 22], missing="4-5, 11-12", ) out = self.stdout().replace(self.last_module_name, "coverage_test") self.assertEqual( out, ( "call: coverage_test.py @ 10\n" "line: coverage_test.py @ 11\n" "line: coverage_test.py @ 12\n" "return: coverage_test.py @ 12\n" ), ) @pytest.mark.expensive def test_atexit_gettrace(self): # pragma: no metacov # This is not a test of coverage at all, but of our understanding # of this edge-case behavior in various Pythons. if env.METACOV: self.skipTest("Can't set trace functions during meta-coverage") self.make_file("atexit_gettrace.py", """\ import atexit, sys def trace_function(frame, event, arg): return trace_function sys.settrace(trace_function) def show_trace_function(): tfn = sys.gettrace() if tfn is not None: tfn = tfn.__name__ print(tfn) atexit.register(show_trace_function) # This will show what the trace function is at the end of the program. """) status, out = self.run_command_status("python atexit_gettrace.py") self.assertEqual(status, 0) if env.PYPY and env.PYPYVERSION >= (5, 4): # Newer PyPy clears the trace function before atexit runs. self.assertEqual(out, "None\n") else: # Other Pythons leave the trace function in place. self.assertEqual(out, "trace_function\n") class ExecTest(CoverageTest): """Tests of exec.""" def test_correct_filename(self): # https://bitbucket.org/ned/coveragepy/issues/380/code-executed-by-exec-excluded-from # Bug was that exec'd files would have their lines attributed to the # calling file. Make two files, both with ~30 lines, but no lines in # common. Line 30 in to_exec.py was recorded as line 30 in main.py, # but now it's fixed. :) self.make_file("to_exec.py", """\ \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n print("var is {0}".format(var)) # line 31 """) self.make_file("main.py", """\ namespace = {'var': 17} with open("to_exec.py") as to_exec_py: code = compile(to_exec_py.read(), 'to_exec.py', 'exec') exec(code, globals(), namespace) \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n print("done") # line 35 """) cov = coverage.Coverage() self.start_import_stop(cov, "main") _, statements, missing, _ = cov.analysis("main.py") self.assertEqual(statements, [1, 2, 3, 4, 35]) self.assertEqual(missing, []) _, statements, missing, _ = cov.analysis("to_exec.py") self.assertEqual(statements, [31]) self.assertEqual(missing, []) class MockingProtectionTest(CoverageTest): """Tests about protecting ourselves from aggressive mocking. https://bitbucket.org/ned/coveragepy/issues/416/coverage-40-is-causing-existing-unit-tests """ def test_os_path_exists(self): # To see if this test still detects the problem, change isolate_module # in misc.py to simply return its argument. It should fail with a # StopIteration error. self.make_file("bug416.py", """\ import os.path import mock @mock.patch('os.path.exists') def test_path_exists(mock_exists): mock_exists.side_effect = [17] print("in test") import bug416a print(bug416a.foo) print(os.path.exists(".")) test_path_exists() """) self.make_file("bug416a.py", """\ print("bug416a.py") foo = 23 """) import py_compile py_compile.compile("bug416a.py") out = self.run_command("coverage run bug416.py") self.assertEqual(out, "in test\nbug416a.py\n23\n17\n") python-coverage-4.5+dfsg.1.orig/tests/test_templite.py0000644000076600000620000002533213173515667021077 0ustar staff# coding: utf-8 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for coverage.templite.""" import re from coverage.templite import Templite, TempliteSyntaxError, TempliteValueError from tests.coveragetest import CoverageTest # pylint: disable=unused-variable class AnyOldObject(object): """Simple testing object. Use keyword arguments in the constructor to set attributes on the object. """ def __init__(self, **attrs): for n, v in attrs.items(): setattr(self, n, v) class TempliteTest(CoverageTest): """Tests for Templite.""" run_in_temp_dir = False def try_render(self, text, ctx=None, result=None): """Render `text` through `ctx`, and it had better be `result`. Result defaults to None so we can shorten the calls where we expect an exception and never get to the result comparison. """ actual = Templite(text).render(ctx or {}) # If result is None, then an exception should have prevented us getting # to here. assert result is not None self.assertEqual(actual, result) def assertSynErr(self, msg): """Assert that a `TempliteSyntaxError` will happen. A context manager, and the message should be `msg`. """ pat = "^" + re.escape(msg) + "$" return self.assertRaisesRegex(TempliteSyntaxError, pat) def test_passthrough(self): # Strings without variables are passed through unchanged. self.assertEqual(Templite("Hello").render(), "Hello") self.assertEqual( Templite("Hello, 20% fun time!").render(), "Hello, 20% fun time!" ) def test_variables(self): # Variables use {{var}} syntax. self.try_render("Hello, {{name}}!", {'name':'Ned'}, "Hello, Ned!") def test_undefined_variables(self): # Using undefined names is an error. with self.assertRaises(Exception): self.try_render("Hi, {{name}}!") def test_pipes(self): # Variables can be filtered with pipes. data = { 'name': 'Ned', 'upper': lambda x: x.upper(), 'second': lambda x: x[1], } self.try_render("Hello, {{name|upper}}!", data, "Hello, NED!") # Pipes can be concatenated. self.try_render("Hello, {{name|upper|second}}!", data, "Hello, E!") def test_reusability(self): # A single Templite can be used more than once with different data. globs = { 'upper': lambda x: x.upper(), 'punct': '!', } template = Templite("This is {{name|upper}}{{punct}}", globs) self.assertEqual(template.render({'name':'Ned'}), "This is NED!") self.assertEqual(template.render({'name':'Ben'}), "This is BEN!") def test_attribute(self): # Variables' attributes can be accessed with dots. obj = AnyOldObject(a="Ay") self.try_render("{{obj.a}}", locals(), "Ay") obj2 = AnyOldObject(obj=obj, b="Bee") self.try_render("{{obj2.obj.a}} {{obj2.b}}", locals(), "Ay Bee") def test_member_function(self): # Variables' member functions can be used, as long as they are nullary. class WithMemberFns(AnyOldObject): """A class to try out member function access.""" def ditto(self): """Return twice the .txt attribute.""" return self.txt + self.txt obj = WithMemberFns(txt="Once") self.try_render("{{obj.ditto}}", locals(), "OnceOnce") def test_item_access(self): # Variables' items can be used. d = {'a':17, 'b':23} self.try_render("{{d.a}} < {{d.b}}", locals(), "17 < 23") def test_loops(self): # Loops work like in Django. nums = [1,2,3,4] self.try_render( "Look: {% for n in nums %}{{n}}, {% endfor %}done.", locals(), "Look: 1, 2, 3, 4, done." ) # Loop iterables can be filtered. def rev(l): """Return the reverse of `l`.""" l = l[:] l.reverse() return l self.try_render( "Look: {% for n in nums|rev %}{{n}}, {% endfor %}done.", locals(), "Look: 4, 3, 2, 1, done." ) def test_empty_loops(self): self.try_render( "Empty: {% for n in nums %}{{n}}, {% endfor %}done.", {'nums':[]}, "Empty: done." ) def test_multiline_loops(self): self.try_render( "Look: \n{% for n in nums %}\n{{n}}, \n{% endfor %}done.", {'nums':[1,2,3]}, "Look: \n\n1, \n\n2, \n\n3, \ndone." ) def test_multiple_loops(self): self.try_render( "{% for n in nums %}{{n}}{% endfor %} and " "{% for n in nums %}{{n}}{% endfor %}", {'nums': [1,2,3]}, "123 and 123" ) def test_comments(self): # Single-line comments work: self.try_render( "Hello, {# Name goes here: #}{{name}}!", {'name':'Ned'}, "Hello, Ned!" ) # and so do multi-line comments: self.try_render( "Hello, {# Name\ngoes\nhere: #}{{name}}!", {'name':'Ned'}, "Hello, Ned!" ) def test_if(self): self.try_render( "Hi, {% if ned %}NED{% endif %}{% if ben %}BEN{% endif %}!", {'ned': 1, 'ben': 0}, "Hi, NED!" ) self.try_render( "Hi, {% if ned %}NED{% endif %}{% if ben %}BEN{% endif %}!", {'ned': 0, 'ben': 1}, "Hi, BEN!" ) self.try_render( "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!", {'ned': 0, 'ben': 0}, "Hi, !" ) self.try_render( "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!", {'ned': 1, 'ben': 0}, "Hi, NED!" ) self.try_render( "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!", {'ned': 1, 'ben': 1}, "Hi, NEDBEN!" ) def test_complex_if(self): class Complex(AnyOldObject): """A class to try out complex data access.""" def getit(self): """Return it.""" return self.it obj = Complex(it={'x':"Hello", 'y': 0}) self.try_render( "@" "{% if obj.getit.x %}X{% endif %}" "{% if obj.getit.y %}Y{% endif %}" "{% if obj.getit.y|str %}S{% endif %}" "!", { 'obj': obj, 'str': str }, "@XS!" ) def test_loop_if(self): self.try_render( "@{% for n in nums %}{% if n %}Z{% endif %}{{n}}{% endfor %}!", {'nums': [0,1,2]}, "@0Z1Z2!" ) self.try_render( "X{%if nums%}@{% for n in nums %}{{n}}{% endfor %}{%endif%}!", {'nums': [0,1,2]}, "X@012!" ) self.try_render( "X{%if nums%}@{% for n in nums %}{{n}}{% endfor %}{%endif%}!", {'nums': []}, "X!" ) def test_nested_loops(self): self.try_render( "@" "{% for n in nums %}" "{% for a in abc %}{{a}}{{n}}{% endfor %}" "{% endfor %}" "!", {'nums': [0,1,2], 'abc': ['a', 'b', 'c']}, "@a0b0c0a1b1c1a2b2c2!" ) def test_whitespace_handling(self): self.try_render( "@{% for n in nums %}\n" " {% for a in abc %}{{a}}{{n}}{% endfor %}\n" "{% endfor %}!\n", {'nums': [0, 1, 2], 'abc': ['a', 'b', 'c']}, "@\n a0b0c0\n\n a1b1c1\n\n a2b2c2\n!\n" ) self.try_render( "@{% for n in nums -%}\n" " {% for a in abc -%}\n" " {# this disappears completely -#}\n" " {{a -}}\n" " {{n -}}\n" " {% endfor %}\n" "{% endfor %}!\n", {'nums': [0, 1, 2], 'abc': ['a', 'b', 'c']}, "@a0b0c0\na1b1c1\na2b2c2\n!\n" ) def test_non_ascii(self): self.try_render( u"{{where}} ollǝɥ", { 'where': u'ǝɹǝɥʇ' }, u"ǝɹǝɥʇ ollǝɥ" ) def test_exception_during_evaluation(self): # TypeError: Couldn't evaluate {{ foo.bar.baz }}: msg = "Couldn't evaluate None.bar" with self.assertRaisesRegex(TempliteValueError, msg): self.try_render( "Hey {{foo.bar.baz}} there", {'foo': None}, "Hey ??? there" ) def test_bad_names(self): with self.assertSynErr("Not a valid name: 'var%&!@'"): self.try_render("Wat: {{ var%&!@ }}") with self.assertSynErr("Not a valid name: 'filter%&!@'"): self.try_render("Wat: {{ foo|filter%&!@ }}") with self.assertSynErr("Not a valid name: '@'"): self.try_render("Wat: {% for @ in x %}{% endfor %}") def test_bogus_tag_syntax(self): with self.assertSynErr("Don't understand tag: 'bogus'"): self.try_render("Huh: {% bogus %}!!{% endbogus %}??") def test_malformed_if(self): with self.assertSynErr("Don't understand if: '{% if %}'"): self.try_render("Buh? {% if %}hi!{% endif %}") with self.assertSynErr("Don't understand if: '{% if this or that %}'"): self.try_render("Buh? {% if this or that %}hi!{% endif %}") def test_malformed_for(self): with self.assertSynErr("Don't understand for: '{% for %}'"): self.try_render("Weird: {% for %}loop{% endfor %}") with self.assertSynErr("Don't understand for: '{% for x from y %}'"): self.try_render("Weird: {% for x from y %}loop{% endfor %}") with self.assertSynErr("Don't understand for: '{% for x, y in z %}'"): self.try_render("Weird: {% for x, y in z %}loop{% endfor %}") def test_bad_nesting(self): with self.assertSynErr("Unmatched action tag: 'if'"): self.try_render("{% if x %}X") with self.assertSynErr("Mismatched end tag: 'for'"): self.try_render("{% if x %}X{% endfor %}") with self.assertSynErr("Too many ends: '{% endif %}'"): self.try_render("{% if x %}{% endif %}{% endif %}") def test_malformed_end(self): with self.assertSynErr("Don't understand end: '{% end if %}'"): self.try_render("{% if x %}X{% end if %}") with self.assertSynErr("Don't understand end: '{% endif now %}'"): self.try_render("{% if x %}X{% endif now %}") python-coverage-4.5+dfsg.1.orig/tests/stress_phystoken_dos.tok0000644000076600000620000000220413146037571022640 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Here's some random Python so that test_tokenize_myself will have some # stressful stuff to try. This file is .tok instead of .py so pylint won't # complain about it, check_eol won't look at it, etc. first_back = """\ hey there! """ other_back = """ hey \ there """ lots_of_back = """\ hey \ there """ # This next line is supposed to have trailing whitespace: fake_back = """\ ouch """ # Lots of difficulty happens with code like: # # fake_back = """\ # ouch # """ # # Ugh, the edge cases... # What about a comment like this\ "what's this string doing here?" class C(object): def there(): this = 5 + \ 7 that = \ "a continued line" cont1 = "one line of text" + \ "another line of text" a_long_string = \ "part 1" \ "2" \ "3 is longer" def hello(): print("Hello world!") hello() python-coverage-4.5+dfsg.1.orig/tests/test_data.py0000644000076600000620000006711413231106622020147 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for coverage.data""" import glob import json import os import os.path import re import mock from coverage.backward import StringIO from coverage.data import CoverageData, CoverageDataFiles, debug_main, canonicalize_json_data from coverage.debug import DebugControlString from coverage.files import PathAliases, canonical_filename from coverage.misc import CoverageException from tests.coveragetest import CoverageTest LINES_1 = { 'a.py': {1: None, 2: None}, 'b.py': {3: None}, } SUMMARY_1 = {'a.py': 2, 'b.py': 1} MEASURED_FILES_1 = ['a.py', 'b.py'] A_PY_LINES_1 = [1, 2] B_PY_LINES_1 = [3] LINES_2 = { 'a.py': {1: None, 5: None}, 'c.py': {17: None}, } SUMMARY_1_2 = {'a.py': 3, 'b.py': 1, 'c.py': 1} MEASURED_FILES_1_2 = ['a.py', 'b.py', 'c.py'] ARCS_3 = { 'x.py': { (-1, 1): None, (1, 2): None, (2, 3): None, (3, -1): None, }, 'y.py': { (-1, 17): None, (17, 23): None, (23, -1): None, }, } X_PY_ARCS_3 = [(-1, 1), (1, 2), (2, 3), (3, -1)] Y_PY_ARCS_3 = [(-1, 17), (17, 23), (23, -1)] SUMMARY_3 = {'x.py': 3, 'y.py': 2} MEASURED_FILES_3 = ['x.py', 'y.py'] X_PY_LINES_3 = [1, 2, 3] Y_PY_LINES_3 = [17, 23] ARCS_4 = { 'x.py': { (-1, 2): None, (2, 5): None, (5, -1): None, }, 'z.py': { (-1, 1000): None, (1000, -1): None, }, } SUMMARY_3_4 = {'x.py': 4, 'y.py': 2, 'z.py': 1} MEASURED_FILES_3_4 = ['x.py', 'y.py', 'z.py'] class DataTestHelpers(CoverageTest): """Test helpers for data tests.""" def assert_line_counts(self, covdata, line_counts, fullpath=False): """Check that the line_counts of `covdata` is `line_counts`.""" self.assertEqual(covdata.line_counts(fullpath), line_counts) def assert_measured_files(self, covdata, measured): """Check that `covdata`'s measured files are `measured`.""" self.assertCountEqual(covdata.measured_files(), measured) def assert_lines1_data(self, covdata): """Check that `covdata` has the data from LINES1.""" self.assert_line_counts(covdata, SUMMARY_1) self.assert_measured_files(covdata, MEASURED_FILES_1) self.assertCountEqual(covdata.lines("a.py"), A_PY_LINES_1) self.assertEqual(covdata.run_infos(), []) self.assertFalse(covdata.has_arcs()) def assert_arcs3_data(self, covdata): """Check that `covdata` has the data from ARCS3.""" self.assert_line_counts(covdata, SUMMARY_3) self.assert_measured_files(covdata, MEASURED_FILES_3) self.assertCountEqual(covdata.lines("x.py"), X_PY_LINES_3) self.assertCountEqual(covdata.arcs("x.py"), X_PY_ARCS_3) self.assertCountEqual(covdata.lines("y.py"), Y_PY_LINES_3) self.assertCountEqual(covdata.arcs("y.py"), Y_PY_ARCS_3) self.assertTrue(covdata.has_arcs()) self.assertEqual(covdata.run_infos(), []) class CoverageDataTest(DataTestHelpers, CoverageTest): """Test cases for CoverageData.""" run_in_temp_dir = False def test_empty_data_is_false(self): covdata = CoverageData() self.assertFalse(covdata) def test_line_data_is_true(self): covdata = CoverageData() covdata.add_lines(LINES_1) self.assertTrue(covdata) def test_arc_data_is_true(self): covdata = CoverageData() covdata.add_arcs(ARCS_3) self.assertTrue(covdata) def test_empty_line_data_is_false(self): covdata = CoverageData() covdata.add_lines({}) self.assertFalse(covdata) def test_empty_arc_data_is_false(self): covdata = CoverageData() covdata.add_arcs({}) self.assertFalse(covdata) def test_adding_lines(self): covdata = CoverageData() covdata.add_lines(LINES_1) self.assert_lines1_data(covdata) def test_adding_arcs(self): covdata = CoverageData() covdata.add_arcs(ARCS_3) self.assert_arcs3_data(covdata) def test_ok_to_add_lines_twice(self): covdata = CoverageData() covdata.add_lines(LINES_1) covdata.add_lines(LINES_2) self.assert_line_counts(covdata, SUMMARY_1_2) self.assert_measured_files(covdata, MEASURED_FILES_1_2) def test_ok_to_add_arcs_twice(self): covdata = CoverageData() covdata.add_arcs(ARCS_3) covdata.add_arcs(ARCS_4) self.assert_line_counts(covdata, SUMMARY_3_4) self.assert_measured_files(covdata, MEASURED_FILES_3_4) def test_cant_add_arcs_with_lines(self): covdata = CoverageData() covdata.add_lines(LINES_1) with self.assertRaisesRegex(CoverageException, "Can't add arcs to existing line data"): covdata.add_arcs(ARCS_3) def test_cant_add_lines_with_arcs(self): covdata = CoverageData() covdata.add_arcs(ARCS_3) with self.assertRaisesRegex(CoverageException, "Can't add lines to existing arc data"): covdata.add_lines(LINES_1) def test_touch_file_with_lines(self): covdata = CoverageData() covdata.add_lines(LINES_1) covdata.touch_file('zzz.py') self.assert_measured_files(covdata, MEASURED_FILES_1 + ['zzz.py']) def test_touch_file_with_arcs(self): covdata = CoverageData() covdata.add_arcs(ARCS_3) covdata.touch_file('zzz.py') self.assert_measured_files(covdata, MEASURED_FILES_3 + ['zzz.py']) def test_no_lines_vs_unmeasured_file(self): covdata = CoverageData() covdata.add_lines(LINES_1) covdata.touch_file('zzz.py') self.assertEqual(covdata.lines('zzz.py'), []) self.assertIsNone(covdata.lines('no_such_file.py')) def test_run_info(self): covdata = CoverageData() self.assertEqual(covdata.run_infos(), []) covdata.add_run_info(hello="there") self.assertEqual(covdata.run_infos(), [{"hello": "there"}]) covdata.add_run_info(count=17) self.assertEqual(covdata.run_infos(), [{"hello": "there", "count": 17}]) def test_no_arcs_vs_unmeasured_file(self): covdata = CoverageData() covdata.add_arcs(ARCS_3) covdata.touch_file('zzz.py') self.assertEqual(covdata.lines('zzz.py'), []) self.assertIsNone(covdata.lines('no_such_file.py')) self.assertEqual(covdata.arcs('zzz.py'), []) self.assertIsNone(covdata.arcs('no_such_file.py')) def test_file_tracer_name(self): covdata = CoverageData() covdata.add_lines({ "p1.foo": dict.fromkeys([1, 2, 3]), "p2.html": dict.fromkeys([10, 11, 12]), "main.py": dict.fromkeys([20]), }) covdata.add_file_tracers({"p1.foo": "p1.plugin", "p2.html": "p2.plugin"}) self.assertEqual(covdata.file_tracer("p1.foo"), "p1.plugin") self.assertEqual(covdata.file_tracer("main.py"), "") self.assertIsNone(covdata.file_tracer("p3.not_here")) def test_cant_file_tracer_unmeasured_files(self): covdata = CoverageData() msg = "Can't add file tracer data for unmeasured file 'p1.foo'" with self.assertRaisesRegex(CoverageException, msg): covdata.add_file_tracers({"p1.foo": "p1.plugin"}) covdata.add_lines({"p2.html": dict.fromkeys([10, 11, 12])}) with self.assertRaisesRegex(CoverageException, msg): covdata.add_file_tracers({"p1.foo": "p1.plugin"}) def test_cant_change_file_tracer_name(self): covdata = CoverageData() covdata.add_lines({"p1.foo": dict.fromkeys([1, 2, 3])}) covdata.add_file_tracers({"p1.foo": "p1.plugin"}) msg = "Conflicting file tracer name for 'p1.foo': 'p1.plugin' vs 'p1.plugin.foo'" with self.assertRaisesRegex(CoverageException, msg): covdata.add_file_tracers({"p1.foo": "p1.plugin.foo"}) def test_update_lines(self): covdata1 = CoverageData() covdata1.add_lines(LINES_1) covdata2 = CoverageData() covdata2.add_lines(LINES_2) covdata3 = CoverageData() covdata3.update(covdata1) covdata3.update(covdata2) self.assert_line_counts(covdata3, SUMMARY_1_2) self.assert_measured_files(covdata3, MEASURED_FILES_1_2) self.assertEqual(covdata3.run_infos(), []) def test_update_arcs(self): covdata1 = CoverageData() covdata1.add_arcs(ARCS_3) covdata2 = CoverageData() covdata2.add_arcs(ARCS_4) covdata3 = CoverageData() covdata3.update(covdata1) covdata3.update(covdata2) self.assert_line_counts(covdata3, SUMMARY_3_4) self.assert_measured_files(covdata3, MEASURED_FILES_3_4) self.assertEqual(covdata3.run_infos(), []) def test_update_run_info(self): covdata1 = CoverageData() covdata1.add_arcs(ARCS_3) covdata1.add_run_info(hello="there", count=17) covdata2 = CoverageData() covdata2.add_arcs(ARCS_4) covdata2.add_run_info(hello="goodbye", count=23) covdata3 = CoverageData() covdata3.update(covdata1) covdata3.update(covdata2) self.assertEqual(covdata3.run_infos(), [ {'hello': 'there', 'count': 17}, {'hello': 'goodbye', 'count': 23}, ]) def test_update_cant_mix_lines_and_arcs(self): covdata1 = CoverageData() covdata1.add_lines(LINES_1) covdata2 = CoverageData() covdata2.add_arcs(ARCS_3) with self.assertRaisesRegex(CoverageException, "Can't combine arc data with line data"): covdata1.update(covdata2) with self.assertRaisesRegex(CoverageException, "Can't combine line data with arc data"): covdata2.update(covdata1) def test_update_file_tracers(self): covdata1 = CoverageData() covdata1.add_lines({ "p1.html": dict.fromkeys([1, 2, 3, 4]), "p2.html": dict.fromkeys([5, 6, 7]), "main.py": dict.fromkeys([10, 11, 12]), }) covdata1.add_file_tracers({ "p1.html": "html.plugin", "p2.html": "html.plugin2", }) covdata2 = CoverageData() covdata2.add_lines({ "p1.html": dict.fromkeys([3, 4, 5, 6]), "p2.html": dict.fromkeys([7, 8, 9]), "p3.foo": dict.fromkeys([1000, 1001]), "main.py": dict.fromkeys([10, 11, 12]), }) covdata2.add_file_tracers({ "p1.html": "html.plugin", "p2.html": "html.plugin2", "p3.foo": "foo_plugin", }) covdata3 = CoverageData() covdata3.update(covdata1) covdata3.update(covdata2) self.assertEqual(covdata3.file_tracer("p1.html"), "html.plugin") self.assertEqual(covdata3.file_tracer("p2.html"), "html.plugin2") self.assertEqual(covdata3.file_tracer("p3.foo"), "foo_plugin") self.assertEqual(covdata3.file_tracer("main.py"), "") def test_update_conflicting_file_tracers(self): covdata1 = CoverageData() covdata1.add_lines({"p1.html": dict.fromkeys([1, 2, 3])}) covdata1.add_file_tracers({"p1.html": "html.plugin"}) covdata2 = CoverageData() covdata2.add_lines({"p1.html": dict.fromkeys([1, 2, 3])}) covdata2.add_file_tracers({"p1.html": "html.other_plugin"}) msg = "Conflicting file tracer name for 'p1.html': 'html.plugin' vs 'html.other_plugin'" with self.assertRaisesRegex(CoverageException, msg): covdata1.update(covdata2) msg = "Conflicting file tracer name for 'p1.html': 'html.other_plugin' vs 'html.plugin'" with self.assertRaisesRegex(CoverageException, msg): covdata2.update(covdata1) def test_update_file_tracer_vs_no_file_tracer(self): covdata1 = CoverageData() covdata1.add_lines({"p1.html": dict.fromkeys([1, 2, 3])}) covdata1.add_file_tracers({"p1.html": "html.plugin"}) covdata2 = CoverageData() covdata2.add_lines({"p1.html": dict.fromkeys([1, 2, 3])}) msg = "Conflicting file tracer name for 'p1.html': 'html.plugin' vs ''" with self.assertRaisesRegex(CoverageException, msg): covdata1.update(covdata2) msg = "Conflicting file tracer name for 'p1.html': '' vs 'html.plugin'" with self.assertRaisesRegex(CoverageException, msg): covdata2.update(covdata1) def test_add_to_hash_with_lines(self): covdata = CoverageData() covdata.add_lines(LINES_1) hasher = mock.Mock() covdata.add_to_hash("a.py", hasher) self.assertEqual(hasher.method_calls, [ mock.call.update([1, 2]), # lines mock.call.update(""), # file_tracer name ]) def test_add_to_hash_with_arcs(self): covdata = CoverageData() covdata.add_arcs(ARCS_3) covdata.add_file_tracers({"y.py": "hologram_plugin"}) hasher = mock.Mock() covdata.add_to_hash("y.py", hasher) self.assertEqual(hasher.method_calls, [ mock.call.update([(-1, 17), (17, 23), (23, -1)]), # arcs mock.call.update("hologram_plugin"), # file_tracer name ]) def test_add_to_lines_hash_with_missing_file(self): # https://bitbucket.org/ned/coveragepy/issues/403 covdata = CoverageData() covdata.add_lines(LINES_1) hasher = mock.Mock() covdata.add_to_hash("missing.py", hasher) self.assertEqual(hasher.method_calls, [ mock.call.update([]), mock.call.update(None), ]) def test_add_to_arcs_hash_with_missing_file(self): # https://bitbucket.org/ned/coveragepy/issues/403 covdata = CoverageData() covdata.add_arcs(ARCS_3) covdata.add_file_tracers({"y.py": "hologram_plugin"}) hasher = mock.Mock() covdata.add_to_hash("missing.py", hasher) self.assertEqual(hasher.method_calls, [ mock.call.update([]), mock.call.update(None), ]) def test_empty_lines_are_still_lines(self): covdata = CoverageData() covdata.add_lines({}) covdata.touch_file("abc.py") self.assertFalse(covdata.has_arcs()) def test_empty_arcs_are_still_arcs(self): covdata = CoverageData() covdata.add_arcs({}) covdata.touch_file("abc.py") self.assertTrue(covdata.has_arcs()) def test_read_and_write_are_opposites(self): covdata1 = CoverageData() covdata1.add_arcs(ARCS_3) stringio = StringIO() covdata1.write_fileobj(stringio) stringio.seek(0) covdata2 = CoverageData() covdata2.read_fileobj(stringio) self.assert_arcs3_data(covdata2) class CoverageDataTestInTempDir(DataTestHelpers, CoverageTest): """Tests of CoverageData that need a temporary directory to make files.""" def test_read_write_lines(self): covdata1 = CoverageData() covdata1.add_lines(LINES_1) covdata1.write_file("lines.dat") covdata2 = CoverageData() covdata2.read_file("lines.dat") self.assert_lines1_data(covdata2) def test_read_write_arcs(self): covdata1 = CoverageData() covdata1.add_arcs(ARCS_3) covdata1.write_file("arcs.dat") covdata2 = CoverageData() covdata2.read_file("arcs.dat") self.assert_arcs3_data(covdata2) def test_read_errors(self): covdata = CoverageData() msg = r"Couldn't read data from '{0}': \S+" self.make_file("xyzzy.dat", "xyzzy") with self.assertRaisesRegex(CoverageException, msg.format("xyzzy.dat")): covdata.read_file("xyzzy.dat") self.make_file("empty.dat", "") with self.assertRaisesRegex(CoverageException, msg.format("empty.dat")): covdata.read_file("empty.dat") with self.assertRaisesRegex(CoverageException, msg.format("nonexistent.dat")): covdata.read_file("nonexistent.dat") self.make_file("misleading.dat", CoverageData._GO_AWAY + " this isn't JSON") with self.assertRaisesRegex(CoverageException, msg.format("misleading.dat")): covdata.read_file("misleading.dat") # After all that, no data should be in our CoverageData. self.assertFalse(covdata) def test_debug_main(self): covdata1 = CoverageData() covdata1.add_lines(LINES_1) covdata1.write_file(".coverage") debug_main([]) covdata2 = CoverageData() covdata2.add_arcs(ARCS_3) covdata2.add_file_tracers({"y.py": "magic_plugin"}) covdata2.add_run_info(version="v3.14", chunks=["z", "a"]) covdata2.write_file("arcs.dat") covdata3 = CoverageData() covdata3.write_file("empty.dat") debug_main(["arcs.dat", "empty.dat"]) expected = { ".coverage": { "lines": { "a.py": [1, 2], "b.py": [3], }, }, "arcs.dat": { "arcs": { "x.py": [[-1, 1], [1, 2], [2, 3], [3, -1]], "y.py": [[-1, 17], [17, 23], [23, -1]], }, "file_tracers": {"y.py": "magic_plugin"}, "runs": [ { "chunks": ["z", "a"], "version": "v3.14", }, ], }, "empty.dat": {}, } pieces = re.split(r"(?m)-+ ([\w.]+) -+$", self.stdout()) for name, json_out in zip(pieces[1::2], pieces[2::2]): json_got = json.loads(json_out) canonicalize_json_data(json_got) self.assertEqual(expected[name], json_got) class CoverageDataFilesTest(DataTestHelpers, CoverageTest): """Tests of CoverageDataFiles.""" no_files_in_temp_dir = True def setUp(self): super(CoverageDataFilesTest, self).setUp() self.data_files = CoverageDataFiles() def test_reading_missing(self): self.assert_doesnt_exist(".coverage") covdata = CoverageData() self.data_files.read(covdata) self.assert_line_counts(covdata, {}) def test_writing_and_reading(self): covdata1 = CoverageData() covdata1.add_lines(LINES_1) self.data_files.write(covdata1) covdata2 = CoverageData() self.data_files.read(covdata2) self.assert_line_counts(covdata2, SUMMARY_1) def test_debug_output_with_debug_option(self): # With debug option dataio, we get debug output about reading and # writing files. debug = DebugControlString(options=["dataio"]) covdata1 = CoverageData(debug=debug) covdata1.add_lines(LINES_1) self.data_files.write(covdata1) covdata2 = CoverageData(debug=debug) self.data_files.read(covdata2) self.assert_line_counts(covdata2, SUMMARY_1) self.assertRegex( debug.get_output(), r"^Writing data to '.*\.coverage'\n" r"Reading data from '.*\.coverage'\n$" ) def test_debug_output_without_debug_option(self): # With a debug object, but not the dataio option, we don't get debug # output. debug = DebugControlString(options=[]) covdata1 = CoverageData(debug=debug) covdata1.add_lines(LINES_1) self.data_files.write(covdata1) covdata2 = CoverageData(debug=debug) self.data_files.read(covdata2) self.assert_line_counts(covdata2, SUMMARY_1) self.assertEqual(debug.get_output(), "") def test_explicit_suffix(self): self.assert_doesnt_exist(".coverage.SUFFIX") covdata = CoverageData() covdata.add_lines(LINES_1) self.data_files.write(covdata, suffix='SUFFIX') self.assert_exists(".coverage.SUFFIX") self.assert_doesnt_exist(".coverage") def test_true_suffix(self): self.assertEqual(glob.glob(".coverage.*"), []) # suffix=True will make a randomly named data file. covdata1 = CoverageData() covdata1.add_lines(LINES_1) self.data_files.write(covdata1, suffix=True) self.assert_doesnt_exist(".coverage") data_files1 = glob.glob(".coverage.*") self.assertEqual(len(data_files1), 1) # Another suffix=True will choose a different name. covdata2 = CoverageData() covdata2.add_lines(LINES_1) self.data_files.write(covdata2, suffix=True) self.assert_doesnt_exist(".coverage") data_files2 = glob.glob(".coverage.*") self.assertEqual(len(data_files2), 2) # In addition to being different, the suffixes have the pid in them. self.assertTrue(all(str(os.getpid()) in fn for fn in data_files2)) def test_combining(self): self.assert_doesnt_exist(".coverage.1") self.assert_doesnt_exist(".coverage.2") covdata1 = CoverageData() covdata1.add_lines(LINES_1) self.data_files.write(covdata1, suffix='1') self.assert_exists(".coverage.1") self.assert_doesnt_exist(".coverage.2") covdata2 = CoverageData() covdata2.add_lines(LINES_2) self.data_files.write(covdata2, suffix='2') self.assert_exists(".coverage.2") covdata3 = CoverageData() self.data_files.combine_parallel_data(covdata3) self.assert_line_counts(covdata3, SUMMARY_1_2) self.assert_measured_files(covdata3, MEASURED_FILES_1_2) self.assert_doesnt_exist(".coverage.1") self.assert_doesnt_exist(".coverage.2") def test_erasing(self): covdata1 = CoverageData() covdata1.add_lines(LINES_1) self.data_files.write(covdata1) covdata1.erase() self.assert_line_counts(covdata1, {}) self.data_files.erase() covdata2 = CoverageData() self.data_files.read(covdata2) self.assert_line_counts(covdata2, {}) def test_erasing_parallel(self): self.make_file("datafile.1") self.make_file("datafile.2") self.make_file(".coverage") data_files = CoverageDataFiles("datafile") data_files.erase(parallel=True) self.assert_doesnt_exist("datafile.1") self.assert_doesnt_exist("datafile.2") self.assert_exists(".coverage") def read_json_data_file(self, fname): """Read a JSON data file for testing the JSON directly.""" with open(fname, 'r') as fdata: go_away = fdata.read(len(CoverageData._GO_AWAY)) self.assertEqual(go_away, CoverageData._GO_AWAY) return json.load(fdata) def test_file_format(self): # Write with CoverageData, then read the JSON explicitly. covdata = CoverageData() covdata.add_lines(LINES_1) self.data_files.write(covdata) data = self.read_json_data_file(".coverage") lines = data['lines'] self.assertCountEqual(lines.keys(), MEASURED_FILES_1) self.assertCountEqual(lines['a.py'], A_PY_LINES_1) self.assertCountEqual(lines['b.py'], B_PY_LINES_1) # If not measuring branches, there's no arcs entry. self.assertNotIn('arcs', data) # If no file tracers were involved, there's no file_tracers entry. self.assertNotIn('file_tracers', data) def test_file_format_with_arcs(self): # Write with CoverageData, then read the JSON explicitly. covdata = CoverageData() covdata.add_arcs(ARCS_3) self.data_files.write(covdata) data = self.read_json_data_file(".coverage") self.assertNotIn('lines', data) arcs = data['arcs'] self.assertCountEqual(arcs.keys(), MEASURED_FILES_3) self.assertCountEqual(arcs['x.py'], map(list, X_PY_ARCS_3)) self.assertCountEqual(arcs['y.py'], map(list, Y_PY_ARCS_3)) # If no file tracers were involved, there's no file_tracers entry. self.assertNotIn('file_tracers', data) def test_writing_to_other_file(self): data_files = CoverageDataFiles(".otherfile") covdata = CoverageData() covdata.add_lines(LINES_1) data_files.write(covdata) self.assert_doesnt_exist(".coverage") self.assert_exists(".otherfile") data_files.write(covdata, suffix="extra") self.assert_exists(".otherfile.extra") self.assert_doesnt_exist(".coverage") def test_combining_with_aliases(self): covdata1 = CoverageData() covdata1.add_lines({ '/home/ned/proj/src/a.py': {1: None, 2: None}, '/home/ned/proj/src/sub/b.py': {3: None}, '/home/ned/proj/src/template.html': {10: None}, }) covdata1.add_file_tracers({ '/home/ned/proj/src/template.html': 'html.plugin', }) self.data_files.write(covdata1, suffix='1') covdata2 = CoverageData() covdata2.add_lines({ r'c:\ned\test\a.py': {4: None, 5: None}, r'c:\ned\test\sub\b.py': {3: None, 6: None}, }) self.data_files.write(covdata2, suffix='2') covdata3 = CoverageData() aliases = PathAliases() aliases.add("/home/ned/proj/src/", "./") aliases.add(r"c:\ned\test", "./") self.data_files.combine_parallel_data(covdata3, aliases=aliases) apy = canonical_filename('./a.py') sub_bpy = canonical_filename('./sub/b.py') template_html = canonical_filename('./template.html') self.assert_line_counts(covdata3, {apy: 4, sub_bpy: 2, template_html: 1}, fullpath=True) self.assert_measured_files(covdata3, [apy, sub_bpy, template_html]) self.assertEqual(covdata3.file_tracer(template_html), 'html.plugin') def test_combining_from_different_directories(self): covdata1 = CoverageData() covdata1.add_lines(LINES_1) os.makedirs('cov1') covdata1.write_file('cov1/.coverage.1') covdata2 = CoverageData() covdata2.add_lines(LINES_2) os.makedirs('cov2') covdata2.write_file('cov2/.coverage.2') # This data won't be included. covdata_xxx = CoverageData() covdata_xxx.add_arcs(ARCS_3) covdata_xxx.write_file('.coverage.xxx') covdata3 = CoverageData() self.data_files.combine_parallel_data(covdata3, data_paths=['cov1', 'cov2']) self.assert_line_counts(covdata3, SUMMARY_1_2) self.assert_measured_files(covdata3, MEASURED_FILES_1_2) self.assert_doesnt_exist("cov1/.coverage.1") self.assert_doesnt_exist("cov2/.coverage.2") self.assert_exists(".coverage.xxx") def test_combining_from_files(self): covdata1 = CoverageData() covdata1.add_lines(LINES_1) os.makedirs('cov1') covdata1.write_file('cov1/.coverage.1') covdata2 = CoverageData() covdata2.add_lines(LINES_2) os.makedirs('cov2') covdata2.write_file('cov2/.coverage.2') # This data won't be included. covdata_xxx = CoverageData() covdata_xxx.add_arcs(ARCS_3) covdata_xxx.write_file('.coverage.xxx') covdata_xxx.write_file('cov2/.coverage.xxx') covdata3 = CoverageData() self.data_files.combine_parallel_data(covdata3, data_paths=['cov1', 'cov2/.coverage.2']) self.assert_line_counts(covdata3, SUMMARY_1_2) self.assert_measured_files(covdata3, MEASURED_FILES_1_2) self.assert_doesnt_exist("cov1/.coverage.1") self.assert_doesnt_exist("cov2/.coverage.2") self.assert_exists(".coverage.xxx") self.assert_exists("cov2/.coverage.xxx") def test_combining_from_nonexistent_directories(self): covdata = CoverageData() msg = "Couldn't combine from non-existent path 'xyzzy'" with self.assertRaisesRegex(CoverageException, msg): self.data_files.combine_parallel_data(covdata, data_paths=['xyzzy']) python-coverage-4.5+dfsg.1.orig/tests/js/0000755000076600000620000000000013235414516016240 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/js/tests.js0000644000076600000620000001303213146037571017742 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ // Tests of coverage.py HTML report chunk navigation. /*global coverage, jQuery, $ */ // Test helpers function selection_is(assert, sel) { raw_selection_is(assert, sel, true); } function raw_selection_is(assert, sel, check_highlight) { var beg = sel[0], end = sel[1]; assert.equal(coverage.sel_begin, beg); assert.equal(coverage.sel_end, end); if (check_highlight) { assert.equal(coverage.code_container().find(".highlight").length, end-beg); } } function build_fixture(spec) { var i, data; $("#fixture-template").tmpl().appendTo("#qunit-fixture"); for (i = 0; i < spec.length; i++) { data = {number: i+1, klass: spec.substr(i, 1)}; $("#lineno-template").tmpl(data).appendTo("#qunit-fixture .linenos"); $("#text-template").tmpl(data).appendTo("#qunit-fixture .text"); } coverage.pyfile_ready(jQuery); } // Tests // Zero-chunk tests QUnit.module("Zero-chunk navigation", { beforeEach: function () { build_fixture("wwww"); } }); QUnit.test("set_sel defaults", function (assert) { coverage.set_sel(2); assert.equal(coverage.sel_begin, 2); assert.equal(coverage.sel_end, 3); }); QUnit.test("No first chunk to select", function (assert) { coverage.to_first_chunk(); assert.expect(0); }); // One-chunk tests $.each([ ['rrrrr', [1,6]], ['r', [1,2]], ['wwrrrr', [3,7]], ['wwrrrrww', [3,7]], ['rrrrww', [1,5]] ], function (i, params) { // Each of these tests uses a fixture with one highlighted chunks. var id = params[0]; var c1 = params[1]; QUnit.module("One-chunk navigation - " + id, { beforeEach: function () { build_fixture(id); } }); QUnit.test("First chunk", function (assert) { coverage.to_first_chunk(); selection_is(assert, c1); }); QUnit.test("Next chunk is first chunk", function (assert) { coverage.to_next_chunk(); selection_is(assert, c1); }); QUnit.test("There is no next chunk", function (assert) { coverage.to_first_chunk(); coverage.to_next_chunk(); selection_is(assert, c1); }); QUnit.test("There is no prev chunk", function (assert) { coverage.to_first_chunk(); coverage.to_prev_chunk(); selection_is(assert, c1); }); }); // Two-chunk tests $.each([ ['rrwwrrrr', [1,3], [5,9]], ['rb', [1,2], [2,3]], ['rbbbbbbbbbb', [1,2], [2,12]], ['rrrrrrrrrrb', [1,11], [11,12]], ['wrrwrrrrw', [2,4], [5,9]], ['rrrbbb', [1,4], [4,7]] ], function (i, params) { // Each of these tests uses a fixture with two highlighted chunks. var id = params[0]; var c1 = params[1]; var c2 = params[2]; QUnit.module("Two-chunk navigation - " + id, { beforeEach: function () { build_fixture(id); } }); QUnit.test("First chunk", function (assert) { coverage.to_first_chunk(); selection_is(assert, c1); }); QUnit.test("Next chunk is first chunk", function (assert) { coverage.to_next_chunk(); selection_is(assert, c1); }); QUnit.test("Move to next chunk", function (assert) { coverage.to_first_chunk(); coverage.to_next_chunk(); selection_is(assert, c2); }); QUnit.test("Move to first chunk", function (assert) { coverage.to_first_chunk(); coverage.to_next_chunk(); coverage.to_first_chunk(); selection_is(assert, c1); }); QUnit.test("Move to previous chunk", function (assert) { coverage.to_first_chunk(); coverage.to_next_chunk(); coverage.to_prev_chunk(); selection_is(assert, c1); }); QUnit.test("Next doesn't move after last chunk", function (assert) { coverage.to_first_chunk(); coverage.to_next_chunk(); coverage.to_next_chunk(); selection_is(assert, c2); }); QUnit.test("Prev doesn't move before first chunk", function (assert) { coverage.to_first_chunk(); coverage.to_next_chunk(); coverage.to_prev_chunk(); coverage.to_prev_chunk(); selection_is(assert, c1); }); }); QUnit.module("Miscellaneous"); QUnit.test("Jump from a line selected", function (assert) { build_fixture("rrwwrr"); coverage.set_sel(3); coverage.to_next_chunk(); selection_is(assert, [5,7]); }); // Tests of select_line_or_chunk. $.each([ // The data for each test: a spec for the fixture to build, and an array // of the selection that will be selected by select_line_or_chunk for // each line in the fixture. ['rrwwrr', [[1,3], [1,3], [3,4], [4,5], [5,7], [5,7]]], ['rb', [[1,2], [2,3]]], ['r', [[1,2]]], ['w', [[1,2]]], ['www', [[1,2], [2,3], [3,4]]], ['wwwrrr', [[1,2], [2,3], [3,4], [4,7], [4,7], [4,7]]], ['rrrwww', [[1,4], [1,4], [1,4], [4,5], [5,6], [6,7]]], ['rrrbbb', [[1,4], [1,4], [1,4], [4,7], [4,7], [4,7]]] ], function (i, params) { // Each of these tests uses a fixture with two highlighted chunks. var id = params[0]; var sels = params[1]; QUnit.module("Select line or chunk - " + id, { beforeEach: function () { build_fixture(id); } }); $.each(sels, function (i, sel) { i++; QUnit.test("Select line " + i, function (assert) { coverage.select_line_or_chunk(i); raw_selection_is(assert, sel); }); }); }); python-coverage-4.5+dfsg.1.orig/tests/js/index.html0000644000076600000620000000327213146037571020244 0ustar staff Coverage.py Javascript Test Suite
python-coverage-4.5+dfsg.1.orig/tests/test_summary.py0000644000076600000620000007764013231114355020742 0ustar staff# coding: utf-8 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Test text-based summary reporting for coverage.py""" import glob import os import os.path import py_compile import re import coverage from coverage import env from coverage.backward import StringIO from coverage.config import CoverageConfig from coverage.control import Coverage from coverage.data import CoverageData from coverage.misc import CoverageException, output_encoding from coverage.summary import SummaryReporter from tests.coveragetest import CoverageTest, TESTS_DIR, UsingModulesMixin class SummaryTest(UsingModulesMixin, CoverageTest): """Tests of the text summary reporting for coverage.py.""" def make_mycode(self): """Make the mycode.py file when needed.""" self.make_file("mycode.py", """\ import covmod1 import covmodzip1 a = 1 print('done') """) def test_report(self): self.make_mycode() out = self.run_command("coverage run mycode.py") self.assertEqual(out, 'done\n') report = self.report_from_command("coverage report") # Name Stmts Miss Cover # ------------------------------------------------------------------ # c:/ned/coverage/tests/modules/covmod1.py 2 0 100% # c:/ned/coverage/tests/zipmods.zip/covmodzip1.py 2 0 100% # mycode.py 4 0 100% # ------------------------------------------------------------------ # TOTAL 8 0 100% self.assertNotIn("/coverage/__init__/", report) self.assertIn("/tests/modules/covmod1.py ", report) self.assertIn("/tests/zipmods.zip/covmodzip1.py ", report) self.assertIn("mycode.py ", report) self.assertEqual(self.last_line_squeezed(report), "TOTAL 8 0 100%") def test_report_just_one(self): # Try reporting just one module self.make_mycode() self.run_command("coverage run mycode.py") report = self.report_from_command("coverage report mycode.py") # Name Stmts Miss Cover # ------------------------------- # mycode.py 4 0 100% self.assertEqual(self.line_count(report), 3) self.assertNotIn("/coverage/", report) self.assertNotIn("/tests/modules/covmod1.py ", report) self.assertNotIn("/tests/zipmods.zip/covmodzip1.py ", report) self.assertIn("mycode.py ", report) self.assertEqual(self.last_line_squeezed(report), "mycode.py 4 0 100%") def test_report_wildcard(self): # Try reporting using wildcards to get the modules. self.make_mycode() self.run_command("coverage run mycode.py") report = self.report_from_command("coverage report my*.py") # Name Stmts Miss Cover # ------------------------------- # mycode.py 4 0 100% self.assertEqual(self.line_count(report), 3) self.assertNotIn("/coverage/", report) self.assertNotIn("/tests/modules/covmod1.py ", report) self.assertNotIn("/tests/zipmods.zip/covmodzip1.py ", report) self.assertIn("mycode.py ", report) self.assertEqual(self.last_line_squeezed(report), "mycode.py 4 0 100%") def test_report_omitting(self): # Try reporting while omitting some modules self.make_mycode() self.run_command("coverage run mycode.py") report = self.report_from_command("coverage report --omit '%s/*'" % TESTS_DIR) # Name Stmts Miss Cover # ------------------------------- # mycode.py 4 0 100% self.assertEqual(self.line_count(report), 3) self.assertNotIn("/coverage/", report) self.assertNotIn("/tests/modules/covmod1.py ", report) self.assertNotIn("/tests/zipmods.zip/covmodzip1.py ", report) self.assertIn("mycode.py ", report) self.assertEqual(self.last_line_squeezed(report), "mycode.py 4 0 100%") def test_report_including(self): # Try reporting while including some modules self.make_mycode() self.run_command("coverage run mycode.py") report = self.report_from_command("coverage report --include=mycode*") # Name Stmts Miss Cover # ------------------------------- # mycode.py 4 0 100% self.assertEqual(self.line_count(report), 3) self.assertNotIn("/coverage/", report) self.assertNotIn("/tests/modules/covmod1.py ", report) self.assertNotIn("/tests/zipmods.zip/covmodzip1.py ", report) self.assertIn("mycode.py ", report) self.assertEqual(self.last_line_squeezed(report), "mycode.py 4 0 100%") def test_run_source_vs_report_include(self): # https://bitbucket.org/ned/coveragepy/issues/621/include-ignored-warning-when-using self.make_file(".coveragerc", """\ [run] source = . [report] include = mod/*,tests/* """) # It should be OK to use that configuration. cov = coverage.Coverage() with self.assert_warnings(cov, []): cov.start() cov.stop() # pragma: nested def test_run_omit_vs_report_omit(self): # https://bitbucket.org/ned/coveragepy/issues/622/report-omit-overwrites-run-omit # report:omit shouldn't clobber run:omit. self.make_mycode() self.make_file(".coveragerc", """\ [run] omit = */covmodzip1.py [report] omit = */covmod1.py """) self.run_command("coverage run mycode.py") # Read the data written, to see that the right files have been omitted from running. covdata = CoverageData() covdata.read_file(".coverage") files = [os.path.basename(p) for p in covdata.measured_files()] self.assertIn("covmod1.py", files) self.assertNotIn("covmodzip1.py", files) def test_report_branches(self): self.make_file("mybranch.py", """\ def branch(x): if x: print("x") return x branch(1) """) out = self.run_command("coverage run --branch mybranch.py") self.assertEqual(out, 'x\n') report = self.report_from_command("coverage report") # Name Stmts Miss Branch BrPart Cover # ----------------------------------------------- # mybranch.py 5 0 2 1 85% self.assertEqual(self.line_count(report), 3) self.assertIn("mybranch.py ", report) self.assertEqual(self.last_line_squeezed(report), "mybranch.py 5 0 2 1 86%") def test_report_show_missing(self): self.make_file("mymissing.py", """\ def missing(x, y): if x: print("x") return x if y: print("y") try: print("z") 1/0 print("Never!") except ZeroDivisionError: pass return x missing(0, 1) """) out = self.run_command("coverage run mymissing.py") self.assertEqual(out, 'y\nz\n') report = self.report_from_command("coverage report --show-missing") # Name Stmts Miss Cover Missing # -------------------------------------------- # mymissing.py 14 3 79% 3-4, 10 self.assertEqual(self.line_count(report), 3) self.assertIn("mymissing.py ", report) self.assertEqual(self.last_line_squeezed(report), "mymissing.py 14 3 79% 3-4, 10") def test_report_show_missing_branches(self): self.make_file("mybranch.py", """\ def branch(x, y): if x: print("x") if y: print("y") branch(1, 1) """) out = self.run_command("coverage run --branch mybranch.py") self.assertEqual(out, 'x\ny\n') report = self.report_from_command("coverage report --show-missing") # Name Stmts Miss Branch BrPart Cover Missing # ---------------------------------------------------------- # mybranch.py 6 0 4 2 80% 2->4, 4->exit self.assertEqual(self.line_count(report), 3) self.assertIn("mybranch.py ", report) self.assertEqual(self.last_line_squeezed(report), "mybranch.py 6 0 4 2 80% 2->4, 4->exit") def test_report_show_missing_branches_and_lines(self): self.make_file("main.py", """\ import mybranch """) self.make_file("mybranch.py", """\ def branch(x, y, z): if x: print("x") if y: print("y") if z: if x and y: print("z") return x branch(1, 1, 0) """) out = self.run_command("coverage run --branch main.py") self.assertEqual(out, 'x\ny\n') report = self.report_from_command("coverage report --show-missing") report_lines = report.splitlines() expected = [ 'Name Stmts Miss Branch BrPart Cover Missing', '---------------------------------------------------------', 'main.py 1 0 0 0 100%', 'mybranch.py 10 2 8 3 61% 7-8, 2->4, 4->6, 6->7', '---------------------------------------------------------', 'TOTAL 11 2 8 3 63%', ] self.assertEqual(report_lines, expected) def test_report_skip_covered_no_branches(self): self.make_file("main.py", """ import not_covered def normal(): print("z") normal() """) self.make_file("not_covered.py", """ def not_covered(): print("n") """) out = self.run_command("coverage run main.py") self.assertEqual(out, "z\n") report = self.report_from_command("coverage report --skip-covered --fail-under=70") # Name Stmts Miss Cover # ------------------------------------ # not_covered.py 2 1 50% # ------------------------------------ # TOTAL 6 1 83% # # 1 file skipped due to complete coverage. self.assertEqual(self.line_count(report), 7, report) squeezed = self.squeezed_lines(report) self.assertEqual(squeezed[2], "not_covered.py 2 1 50%") self.assertEqual(squeezed[4], "TOTAL 6 1 83%") self.assertEqual(squeezed[6], "1 file skipped due to complete coverage.") self.assertEqual(self.last_command_status, 0) def test_report_skip_covered_branches(self): self.make_file("main.py", """ import not_covered, covered def normal(z): if z: print("z") normal(True) normal(False) """) self.make_file("not_covered.py", """ def not_covered(n): if n: print("n") not_covered(True) """) self.make_file("covered.py", """ def foo(): pass foo() """) out = self.run_command("coverage run --branch main.py") self.assertEqual(out, "n\nz\n") report = self.report_from_command("coverage report --skip-covered") # Name Stmts Miss Branch BrPart Cover # -------------------------------------------------- # not_covered.py 4 0 2 1 83% # -------------------------------------------------- # TOTAL 13 0 4 1 94% # # 2 files skipped due to complete coverage. self.assertEqual(self.line_count(report), 7, report) squeezed = self.squeezed_lines(report) self.assertEqual(squeezed[2], "not_covered.py 4 0 2 1 83%") self.assertEqual(squeezed[4], "TOTAL 13 0 4 1 94%") self.assertEqual(squeezed[6], "2 files skipped due to complete coverage.") def test_report_skip_covered_branches_with_totals(self): self.make_file("main.py", """ import not_covered import also_not_run def normal(z): if z: print("z") normal(True) normal(False) """) self.make_file("not_covered.py", """ def not_covered(n): if n: print("n") not_covered(True) """) self.make_file("also_not_run.py", """ def does_not_appear_in_this_film(ni): print("Ni!") """) out = self.run_command("coverage run --branch main.py") self.assertEqual(out, "n\nz\n") report = self.report_from_command("coverage report --skip-covered") # Name Stmts Miss Branch BrPart Cover # -------------------------------------------------- # also_not_run.py 2 1 0 0 50% # not_covered.py 4 0 2 1 83% # -------------------------------------------------- # TOTAL 13 1 4 1 88% # # 1 file skipped due to complete coverage. self.assertEqual(self.line_count(report), 8, report) squeezed = self.squeezed_lines(report) self.assertEqual(squeezed[2], "also_not_run.py 2 1 0 0 50%") self.assertEqual(squeezed[3], "not_covered.py 4 0 2 1 83%") self.assertEqual(squeezed[5], "TOTAL 13 1 4 1 88%") self.assertEqual(squeezed[7], "1 file skipped due to complete coverage.") def test_report_skip_covered_all_files_covered(self): self.make_file("main.py", """ def foo(): pass foo() """) out = self.run_command("coverage run --branch main.py") self.assertEqual(out, "") report = self.report_from_command("coverage report --skip-covered") # Name Stmts Miss Branch BrPart Cover # ------------------------------------------- # # 1 file skipped due to complete coverage. self.assertEqual(self.line_count(report), 4, report) squeezed = self.squeezed_lines(report) self.assertEqual(squeezed[3], "1 file skipped due to complete coverage.") def test_report_skip_covered_longfilename(self): self.make_file("long_______________filename.py", """ def foo(): pass foo() """) out = self.run_command("coverage run --branch long_______________filename.py") self.assertEqual(out, "") report = self.report_from_command("coverage report --skip-covered") # Name Stmts Miss Branch BrPart Cover # ----------------------------------------- # # 1 file skipped due to complete coverage. self.assertEqual(self.line_count(report), 4, report) lines = self.report_lines(report) self.assertEqual(lines[0], "Name Stmts Miss Branch BrPart Cover") squeezed = self.squeezed_lines(report) self.assertEqual(squeezed[3], "1 file skipped due to complete coverage.") def test_report_skip_covered_no_data(self): report = self.report_from_command("coverage report --skip-covered") # Name Stmts Miss Branch BrPart Cover # ------------------------------------------- # No data to report. self.assertEqual(self.line_count(report), 3, report) squeezed = self.squeezed_lines(report) self.assertEqual(squeezed[2], "No data to report.") def test_report_precision(self): self.make_file(".coveragerc", """\ [report] precision = 3 """) self.make_file("main.py", """ import not_covered, covered def normal(z): if z: print("z") normal(True) normal(False) """) self.make_file("not_covered.py", """ def not_covered(n): if n: print("n") not_covered(True) """) self.make_file("covered.py", """ def foo(): pass foo() """) out = self.run_command("coverage run --branch main.py") self.assertEqual(out, "n\nz\n") report = self.report_from_command("coverage report") # Name Stmts Miss Branch BrPart Cover # ------------------------------------------------------ # covered.py 3 0 0 0 100.000% # main.py 6 0 2 0 100.000% # not_covered.py 4 0 2 1 83.333% # ------------------------------------------------------ # TOTAL 13 0 4 1 94.118% self.assertEqual(self.line_count(report), 7, report) squeezed = self.squeezed_lines(report) self.assertEqual(squeezed[2], "covered.py 3 0 0 0 100.000%") self.assertEqual(squeezed[4], "not_covered.py 4 0 2 1 83.333%") self.assertEqual(squeezed[6], "TOTAL 13 0 4 1 94.118%") def test_dotpy_not_python(self): # We run a .py file, and when reporting, we can't parse it as Python. # We should get an error message in the report. self.make_mycode() self.run_command("coverage run mycode.py") self.make_file("mycode.py", "This isn't python at all!") report = self.report_from_command("coverage report mycode.py") # mycode NotPython: Couldn't parse '...' as Python source: 'invalid syntax' at line 1 # Name Stmts Miss Cover # ---------------------------- # No data to report. errmsg = self.squeezed_lines(report)[0] # The actual file name varies run to run. errmsg = re.sub(r"parse '.*mycode.py", "parse 'mycode.py", errmsg) # The actual error message varies version to version errmsg = re.sub(r": '.*' at", ": 'error' at", errmsg) self.assertEqual( errmsg, "mycode.py NotPython: Couldn't parse 'mycode.py' as Python source: 'error' at line 1" ) def test_accenteddotpy_not_python(self): if env.JYTHON: self.skipTest("Jython doesn't like accented file names") # We run a .py file with a non-ascii name, and when reporting, we can't # parse it as Python. We should get an error message in the report. self.make_file(u"accented\xe2.py", "print('accented')") self.run_command(u"coverage run accented\xe2.py") self.make_file(u"accented\xe2.py", "This isn't python at all!") report = self.report_from_command(u"coverage report accented\xe2.py") # xxxx NotPython: Couldn't parse '...' as Python source: 'invalid syntax' at line 1 # Name Stmts Miss Cover # ---------------------------- # No data to report. errmsg = self.squeezed_lines(report)[0] # The actual file name varies run to run. errmsg = re.sub(r"parse '.*(accented.*?\.py)", r"parse '\1", errmsg) # The actual error message varies version to version errmsg = re.sub(r": '.*' at", ": 'error' at", errmsg) expected = ( u"accented\xe2.py NotPython: " u"Couldn't parse 'accented\xe2.py' as Python source: 'error' at line 1" ) if env.PY2: expected = expected.encode(output_encoding()) self.assertEqual(errmsg, expected) def test_dotpy_not_python_ignored(self): # We run a .py file, and when reporting, we can't parse it as Python, # but we've said to ignore errors, so there's no error reported. self.make_mycode() self.run_command("coverage run mycode.py") self.make_file("mycode.py", "This isn't python at all!") report = self.report_from_command("coverage report -i mycode.py") # Name Stmts Miss Cover # ---------------------------- self.assertEqual(self.line_count(report), 3) self.assertIn('No data to report.', report) def test_dothtml_not_python(self): # We run a .html file, and when reporting, we can't parse it as # Python. Since it wasn't .py, no error is reported. # Run an "html" file self.make_file("mycode.html", "a = 1") self.run_command("coverage run mycode.html") # Before reporting, change it to be an HTML file. self.make_file("mycode.html", "

This isn't python at all!

") report = self.report_from_command("coverage report mycode.html") # Name Stmts Miss Cover # ---------------------------- # No data to report. self.assertEqual(self.line_count(report), 3) self.assertIn('No data to report.', report) def get_report(self, cov): """Get the report from `cov`, and canonicalize it.""" repout = StringIO() cov.report(file=repout, show_missing=False) report = repout.getvalue().replace('\\', '/') report = re.sub(r" +", " ", report) return report def test_bug_156_file_not_run_should_be_zero(self): # https://bitbucket.org/ned/coveragepy/issue/156 self.make_file("mybranch.py", """\ def branch(x): if x: print("x") return x branch(1) """) self.make_file("main.py", """\ print("y") """) cov = coverage.Coverage(branch=True, source=["."]) cov.start() import main # pragma: nested # pylint: disable=import-error, unused-variable cov.stop() # pragma: nested report = self.get_report(cov).splitlines() self.assertIn("mybranch.py 5 5 2 0 0%", report) def run_TheCode_and_report_it(self): """A helper for the next few tests.""" cov = coverage.Coverage() cov.start() import TheCode # pragma: nested # pylint: disable=import-error, unused-variable cov.stop() # pragma: nested return self.get_report(cov) def test_bug_203_mixed_case_listed_twice_with_rc(self): self.make_file("TheCode.py", "a = 1\n") self.make_file(".coveragerc", "[run]\nsource = .\n") report = self.run_TheCode_and_report_it() self.assertIn("TheCode", report) self.assertNotIn("thecode", report) def test_bug_203_mixed_case_listed_twice(self): self.make_file("TheCode.py", "a = 1\n") report = self.run_TheCode_and_report_it() self.assertIn("TheCode", report) self.assertNotIn("thecode", report) def test_pyw_files(self): if not env.WINDOWS: self.skipTest(".pyw files are only on Windows.") # https://bitbucket.org/ned/coveragepy/issue/261 self.make_file("start.pyw", """\ import mod print("In start.pyw") """) self.make_file("mod.pyw", """\ print("In mod.pyw") """) cov = coverage.Coverage() cov.start() import start # pragma: nested # pylint: disable=import-error, unused-variable cov.stop() # pragma: nested report = self.get_report(cov) self.assertNotIn("NoSource", report) report = report.splitlines() self.assertIn("start.pyw 2 0 100%", report) self.assertIn("mod.pyw 1 0 100%", report) def test_tracing_pyc_file(self): # Create two Python files. self.make_file("mod.py", "a = 1\n") self.make_file("main.py", "import mod\n") # Make one into a .pyc. py_compile.compile("mod.py") # Run the program. cov = coverage.Coverage() cov.start() import main # pragma: nested # pylint: disable=import-error, unused-variable cov.stop() # pragma: nested report = self.get_report(cov).splitlines() self.assertIn("mod.py 1 0 100%", report) def test_missing_py_file_during_run(self): # PyPy2 doesn't run bare .pyc files. if env.PYPY and env.PY2: self.skipTest("PyPy2 doesn't run bare .pyc files") # Create two Python files. self.make_file("mod.py", "a = 1\n") self.make_file("main.py", "import mod\n") # Make one into a .pyc, and remove the .py. py_compile.compile("mod.py") os.remove("mod.py") # Python 3 puts the .pyc files in a __pycache__ directory, and will # not import from there without source. It will import a .pyc from # the source location though. if env.PY3 and not env.JYTHON: pycs = glob.glob("__pycache__/mod.*.pyc") self.assertEqual(len(pycs), 1) os.rename(pycs[0], "mod.pyc") # Run the program. cov = coverage.Coverage() cov.start() import main # pragma: nested # pylint: disable=import-error, unused-variable cov.stop() # pragma: nested # Put back the missing Python file. self.make_file("mod.py", "a = 1\n") report = self.get_report(cov).splitlines() self.assertIn("mod.py 1 0 100%", report) class SummaryTest2(UsingModulesMixin, CoverageTest): """Another bunch of summary tests.""" # This class exists because tests naturally clump into classes based on the # needs of their setUp, rather than the product features they are testing. # There's probably a better way to organize these. run_in_temp_dir = False def test_empty_files(self): # Shows that empty files like __init__.py are listed as having zero # statements, not one statement. cov = coverage.Coverage(branch=True) cov.start() import usepkgs # pragma: nested # pylint: disable=import-error, unused-variable cov.stop() # pragma: nested repout = StringIO() cov.report(file=repout, show_missing=False) report = repout.getvalue().replace('\\', '/') report = re.sub(r"\s+", " ", report) self.assertIn("tests/modules/pkg1/__init__.py 1 0 0 0 100%", report) self.assertIn("tests/modules/pkg2/__init__.py 0 0 0 0 100%", report) class ReportingReturnValueTest(CoverageTest): """Tests of reporting functions returning values.""" def run_coverage(self): """Run coverage on doit.py and return the coverage object.""" self.make_file("doit.py", """\ a = 1 b = 2 c = 3 d = 4 if a > 10: f = 6 g = 7 """) cov = coverage.Coverage() self.start_import_stop(cov, "doit") return cov def test_report(self): cov = self.run_coverage() val = cov.report(include="*/doit.py") self.assertAlmostEqual(val, 85.7, 1) def test_html(self): cov = self.run_coverage() val = cov.html_report(include="*/doit.py") self.assertAlmostEqual(val, 85.7, 1) def test_xml(self): cov = self.run_coverage() val = cov.xml_report(include="*/doit.py") self.assertAlmostEqual(val, 85.7, 1) class TestSummaryReporterConfiguration(CoverageTest): """Tests of SummaryReporter.""" run_in_temp_dir = False LINES_1 = { os.path.join(TESTS_DIR, "test_api.py"): dict.fromkeys(range(400)), os.path.join(TESTS_DIR, "test_backward.py"): dict.fromkeys(range(20)), os.path.join(TESTS_DIR, "test_coverage.py"): dict.fromkeys(range(15)), } def get_coverage_data(self, lines): """Get a CoverageData object that includes the requested lines.""" data = CoverageData() data.add_lines(lines) return data def get_summary_text(self, coverage_data, options): """Get text output from the SummaryReporter.""" cov = Coverage() cov.start() cov.stop() # pragma: nested cov.data = coverage_data printer = SummaryReporter(cov, options) destination = StringIO() printer.report([], destination) return destination.getvalue() def test_test_data(self): # We use our own test files as test data. Check that our assumptions # about them are still valid. We want the three columns of numbers to # sort in three different orders. data = self.get_coverage_data(self.LINES_1) report = self.get_summary_text(data, CoverageConfig()) print(report) # Name Stmts Miss Cover # -------------------------------------------- # tests/test_api.py 339 155 54% # tests/test_backward.py 13 3 77% # tests/test_coverage.py 234 228 3% # -------------------------------------------- # TOTAL 586 386 34% lines = report.splitlines()[2:-2] self.assertEqual(len(lines), 3) nums = [list(map(int, l.replace('%', '').split()[1:])) for l in lines] # [ # [339, 155, 54], # [ 13, 3, 77], # [234, 228, 3] # ] self.assertTrue(nums[1][0] < nums[2][0] < nums[0][0]) self.assertTrue(nums[1][1] < nums[0][1] < nums[2][1]) self.assertTrue(nums[2][2] < nums[0][2] < nums[1][2]) def test_defaults(self): """Run the report with no configuration options.""" data = self.get_coverage_data(self.LINES_1) opts = CoverageConfig() report = self.get_summary_text(data, opts) self.assertNotIn('Missing', report) self.assertNotIn('Branch', report) def test_print_missing(self): """Run the report printing the missing lines.""" data = self.get_coverage_data(self.LINES_1) opts = CoverageConfig() opts.from_args(show_missing=True) report = self.get_summary_text(data, opts) self.assertIn('Missing', report) self.assertNotIn('Branch', report) def assert_ordering(self, text, *words): """Assert that the `words` appear in order in `text`.""" indexes = list(map(text.find, words)) self.assertEqual( indexes, sorted(indexes), "The words %r don't appear in order in %r" % (words, text) ) def test_sort_report_by_stmts(self): # Sort the text report by the Stmts column. data = self.get_coverage_data(self.LINES_1) opts = CoverageConfig() opts.from_args(sort='Stmts') report = self.get_summary_text(data, opts) self.assert_ordering(report, "test_backward.py", "test_coverage.py", "test_api.py") def test_sort_report_by_missing(self): # Sort the text report by the Missing column. data = self.get_coverage_data(self.LINES_1) opts = CoverageConfig() opts.from_args(sort='Miss') report = self.get_summary_text(data, opts) self.assert_ordering(report, "test_backward.py", "test_api.py", "test_coverage.py") def test_sort_report_by_cover(self): # Sort the text report by the Cover column. data = self.get_coverage_data(self.LINES_1) opts = CoverageConfig() opts.from_args(sort='Cover') report = self.get_summary_text(data, opts) self.assert_ordering(report, "test_coverage.py", "test_api.py", "test_backward.py") def test_sort_report_by_invalid_option(self): # Sort the text report by a nonsense column. data = self.get_coverage_data(self.LINES_1) opts = CoverageConfig() opts.from_args(sort='Xyzzy') msg = "Invalid sorting option: 'Xyzzy'" with self.assertRaisesRegex(CoverageException, msg): self.get_summary_text(data, opts) python-coverage-4.5+dfsg.1.orig/tests/moremodules/0000755000076600000620000000000013235414514020155 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/moremodules/namespace_420/0000755000076600000620000000000013235414514022476 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/moremodules/namespace_420/sub2/0000755000076600000620000000000013235414516023353 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/moremodules/namespace_420/sub2/__init__.py0000644000076600000620000000027013173515667025475 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt sub2 = "namespace_420 sub2" python-coverage-4.5+dfsg.1.orig/tests/moremodules/othermods/0000755000076600000620000000000013235414516022163 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/moremodules/othermods/otherb.py0000644000076600000620000000025013146037571024020 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt q = 3 r = 4 python-coverage-4.5+dfsg.1.orig/tests/moremodules/othermods/othera.py0000644000076600000620000000025013146037571024017 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt o = 1 p = 2 python-coverage-4.5+dfsg.1.orig/tests/moremodules/othermods/__init__.py0000644000076600000620000000000013146037571024265 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/moremodules/othermods/sub/0000755000076600000620000000000013235414516022754 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/moremodules/othermods/sub/osb.py0000644000076600000620000000025013146037571024111 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt u = 7 v = 8 python-coverage-4.5+dfsg.1.orig/tests/moremodules/othermods/sub/__init__.py0000644000076600000620000000000013146037571025056 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/moremodules/othermods/sub/osa.py0000644000076600000620000000025013146037571024110 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt s = 5 t = 6 python-coverage-4.5+dfsg.1.orig/tests/backtest.py0000644000076600000620000000122413146037571020000 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Add things to old Pythons so I can pretend they are newer, for tests.""" # pylint: disable=redefined-builtin # (Redefining built-in blah) # The whole point of this file is to redefine built-ins, so shut up about it. # No more execfile in Py3 try: execfile = execfile except NameError: def execfile(filename, globs): """A Python 3 implementation of execfile.""" with open(filename) as fobj: code = fobj.read() exec(compile(code, filename, 'exec'), globs) python-coverage-4.5+dfsg.1.orig/tests/test_results.py0000644000076600000620000001006513231142116020726 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for coverage.py's results analysis.""" import pytest from coverage.results import Numbers, should_fail_under from tests.coveragetest import CoverageTest class NumbersTest(CoverageTest): """Tests for coverage.py's numeric measurement summaries.""" run_in_temp_dir = False def test_basic(self): n1 = Numbers(n_files=1, n_statements=200, n_missing=20) self.assertEqual(n1.n_statements, 200) self.assertEqual(n1.n_executed, 180) self.assertEqual(n1.n_missing, 20) self.assertEqual(n1.pc_covered, 90) def test_addition(self): n1 = Numbers(n_files=1, n_statements=200, n_missing=20) n2 = Numbers(n_files=1, n_statements=10, n_missing=8) n3 = n1 + n2 self.assertEqual(n3.n_files, 2) self.assertEqual(n3.n_statements, 210) self.assertEqual(n3.n_executed, 182) self.assertEqual(n3.n_missing, 28) self.assertAlmostEqual(n3.pc_covered, 86.666666666) def test_sum(self): n1 = Numbers(n_files=1, n_statements=200, n_missing=20) n2 = Numbers(n_files=1, n_statements=10, n_missing=8) n3 = sum([n1, n2]) self.assertEqual(n3.n_files, 2) self.assertEqual(n3.n_statements, 210) self.assertEqual(n3.n_executed, 182) self.assertEqual(n3.n_missing, 28) self.assertAlmostEqual(n3.pc_covered, 86.666666666) def test_pc_covered_str(self): # Numbers._precision is a global, which is bad. Numbers.set_precision(0) n0 = Numbers(n_files=1, n_statements=1000, n_missing=0) n1 = Numbers(n_files=1, n_statements=1000, n_missing=1) n999 = Numbers(n_files=1, n_statements=1000, n_missing=999) n1000 = Numbers(n_files=1, n_statements=1000, n_missing=1000) self.assertEqual(n0.pc_covered_str, "100") self.assertEqual(n1.pc_covered_str, "99") self.assertEqual(n999.pc_covered_str, "1") self.assertEqual(n1000.pc_covered_str, "0") def test_pc_covered_str_precision(self): # Numbers._precision is a global, which is bad. Numbers.set_precision(1) n0 = Numbers(n_files=1, n_statements=10000, n_missing=0) n1 = Numbers(n_files=1, n_statements=10000, n_missing=1) n9999 = Numbers(n_files=1, n_statements=10000, n_missing=9999) n10000 = Numbers(n_files=1, n_statements=10000, n_missing=10000) self.assertEqual(n0.pc_covered_str, "100.0") self.assertEqual(n1.pc_covered_str, "99.9") self.assertEqual(n9999.pc_covered_str, "0.1") self.assertEqual(n10000.pc_covered_str, "0.0") Numbers.set_precision(0) def test_covered_ratio(self): n = Numbers(n_files=1, n_statements=200, n_missing=47) self.assertEqual(n.ratio_covered, (153, 200)) n = Numbers( n_files=1, n_statements=200, n_missing=47, n_branches=10, n_missing_branches=3, n_partial_branches=1000, ) self.assertEqual(n.ratio_covered, (160, 210)) @pytest.mark.parametrize("total, fail_under, precision, result", [ # fail_under==0 means anything is fine! (0, 0, 0, False), (0.001, 0, 0, False), # very small fail_under is possible to fail. (0.001, 0.01, 0, True), # Rounding should work properly. (42.1, 42, 0, False), (42.1, 43, 0, True), (42.857, 42, 0, False), (42.857, 43, 0, False), (42.857, 44, 0, True), (42.857, 42.856, 3, False), (42.857, 42.858, 3, True), # If you don't specify precision, your fail-under is rounded. (42.857, 42.856, 0, False), # Values near 100 should only be treated as 100 if they are 100. (99.8, 100, 0, True), (100.0, 100, 0, False), (99.8, 99.7, 1, False), (99.88, 99.90, 2, True), (99.999, 100, 1, True), (99.999, 100, 2, True), (99.999, 100, 3, True), ]) def test_should_fail_under(total, fail_under, precision, result): assert should_fail_under(float(total), float(fail_under), precision) == result python-coverage-4.5+dfsg.1.orig/tests/test_plugins.py0000644000076600000620000007607613231521236020730 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for plugins.""" import os.path import coverage from coverage import env from coverage.backward import StringIO from coverage.control import Plugins from coverage.misc import CoverageException import coverage.plugin from tests.coveragetest import CoverageTest from tests.helpers import CheckUniqueFilenames class FakeConfig(object): """A fake config for use in tests.""" def __init__(self, plugin, options): self.plugin = plugin self.options = options self.asked_for = [] def get_plugin_options(self, module): """Just return the options for `module` if this is the right module.""" self.asked_for.append(module) if module == self.plugin: return self.options else: return {} class LoadPluginsTest(CoverageTest): """Test Plugins.load_plugins directly.""" def test_implicit_boolean(self): self.make_file("plugin1.py", """\ from coverage import CoveragePlugin class Plugin(CoveragePlugin): pass def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) config = FakeConfig("plugin1", {}) plugins = Plugins.load_plugins([], config) self.assertFalse(plugins) plugins = Plugins.load_plugins(["plugin1"], config) self.assertTrue(plugins) def test_importing_and_configuring(self): self.make_file("plugin1.py", """\ from coverage import CoveragePlugin class Plugin(CoveragePlugin): def __init__(self, options): self.options = options self.this_is = "me" def coverage_init(reg, options): reg.add_file_tracer(Plugin(options)) """) config = FakeConfig("plugin1", {'a': 'hello'}) plugins = list(Plugins.load_plugins(["plugin1"], config)) self.assertEqual(len(plugins), 1) self.assertEqual(plugins[0].this_is, "me") self.assertEqual(plugins[0].options, {'a': 'hello'}) self.assertEqual(config.asked_for, ['plugin1']) def test_importing_and_configuring_more_than_one(self): self.make_file("plugin1.py", """\ from coverage import CoveragePlugin class Plugin(CoveragePlugin): def __init__(self, options): self.options = options self.this_is = "me" def coverage_init(reg, options): reg.add_file_tracer(Plugin(options)) """) self.make_file("plugin2.py", """\ from coverage import CoveragePlugin class Plugin(CoveragePlugin): def __init__(self, options): self.options = options def coverage_init(reg, options): reg.add_file_tracer(Plugin(options)) """) config = FakeConfig("plugin1", {'a': 'hello'}) plugins = list(Plugins.load_plugins(["plugin1", "plugin2"], config)) self.assertEqual(len(plugins), 2) self.assertEqual(plugins[0].this_is, "me") self.assertEqual(plugins[0].options, {'a': 'hello'}) self.assertEqual(plugins[1].options, {}) self.assertEqual(config.asked_for, ['plugin1', 'plugin2']) # The order matters... config = FakeConfig("plugin1", {'a': 'second'}) plugins = list(Plugins.load_plugins(["plugin2", "plugin1"], config)) self.assertEqual(len(plugins), 2) self.assertEqual(plugins[0].options, {}) self.assertEqual(plugins[1].this_is, "me") self.assertEqual(plugins[1].options, {'a': 'second'}) def test_cant_import(self): with self.assertRaises(ImportError): _ = Plugins.load_plugins(["plugin_not_there"], None) def test_plugin_must_define_coverage_init(self): self.make_file("no_plugin.py", """\ from coverage import CoveragePlugin Nothing = 0 """) msg_pat = "Plugin module 'no_plugin' didn't define a coverage_init function" with self.assertRaisesRegex(CoverageException, msg_pat): list(Plugins.load_plugins(["no_plugin"], None)) class PluginTest(CoverageTest): """Test plugins through the Coverage class.""" def test_plugin_imported(self): # Prove that a plugin will be imported. self.make_file("my_plugin.py", """\ from coverage import CoveragePlugin class Plugin(CoveragePlugin): pass def coverage_init(reg, options): reg.add_noop(Plugin()) with open("evidence.out", "w") as f: f.write("we are here!") """) self.assert_doesnt_exist("evidence.out") cov = coverage.Coverage() cov.set_option("run:plugins", ["my_plugin"]) cov.start() cov.stop() # pragma: nested with open("evidence.out") as f: self.assertEqual(f.read(), "we are here!") def test_missing_plugin_raises_import_error(self): # Prove that a missing plugin will raise an ImportError. with self.assertRaises(ImportError): cov = coverage.Coverage() cov.set_option("run:plugins", ["does_not_exist_woijwoicweo"]) cov.start() cov.stop() def test_bad_plugin_isnt_hidden(self): # Prove that a plugin with an error in it will raise the error. self.make_file("plugin_over_zero.py", """\ 1/0 """) with self.assertRaises(ZeroDivisionError): cov = coverage.Coverage() cov.set_option("run:plugins", ["plugin_over_zero"]) cov.start() cov.stop() def test_plugin_sys_info(self): self.make_file("plugin_sys_info.py", """\ import coverage class Plugin(coverage.CoveragePlugin): def sys_info(self): return [("hello", "world")] def coverage_init(reg, options): reg.add_noop(Plugin()) """) debug_out = StringIO() cov = coverage.Coverage(debug=["sys"]) cov._debug_file = debug_out cov.set_option("run:plugins", ["plugin_sys_info"]) cov.load() out_lines = debug_out.getvalue().splitlines() expected_end = [ "-- sys: plugin_sys_info.Plugin -------------------------------", " hello: world", "-- end -------------------------------------------------------", ] self.assertEqual(expected_end, out_lines[-len(expected_end):]) def test_plugin_with_no_sys_info(self): self.make_file("plugin_no_sys_info.py", """\ import coverage class Plugin(coverage.CoveragePlugin): pass def coverage_init(reg, options): reg.add_noop(Plugin()) """) debug_out = StringIO() cov = coverage.Coverage(debug=["sys"]) cov._debug_file = debug_out cov.set_option("run:plugins", ["plugin_no_sys_info"]) cov.load() out_lines = debug_out.getvalue().splitlines() expected_end = [ "-- sys: plugin_no_sys_info.Plugin ----------------------------", "-- end -------------------------------------------------------", ] self.assertEqual(expected_end, out_lines[-len(expected_end):]) def test_local_files_are_importable(self): self.make_file("importing_plugin.py", """\ from coverage import CoveragePlugin import local_module class MyPlugin(CoveragePlugin): pass def coverage_init(reg, options): reg.add_noop(MyPlugin()) """) self.make_file("local_module.py", "CONST = 1") self.make_file(".coveragerc", """\ [run] plugins = importing_plugin """) self.make_file("main_file.py", "print('MAIN')") out = self.run_command("coverage run main_file.py") self.assertEqual(out, "MAIN\n") out = self.run_command("coverage html") self.assertEqual(out, "") class PluginWarningOnPyTracer(CoverageTest): """Test that we get a controlled exception with plugins on PyTracer.""" def test_exception_if_plugins_on_pytracer(self): if env.C_TRACER: self.skipTest("This test is only about PyTracer.") self.make_file("simple.py", """a = 1""") cov = coverage.Coverage() cov.set_option("run:plugins", ["tests.plugin1"]) expected_warnings = [ r"Plugin file tracers \(tests.plugin1.Plugin\) aren't supported with PyTracer", ] with self.assert_warnings(cov, expected_warnings): self.start_import_stop(cov, "simple") class FileTracerTest(CoverageTest): """Tests of plugins that implement file_tracer.""" def setUp(self): super(FileTracerTest, self).setUp() if not env.C_TRACER: self.skipTest("Plugins are only supported with the C tracer.") class GoodFileTracerTest(FileTracerTest): """Tests of file tracer plugin happy paths.""" def test_plugin1(self): self.make_file("simple.py", """\ import try_xyz a = 1 b = 2 """) self.make_file("try_xyz.py", """\ c = 3 d = 4 """) cov = coverage.Coverage() CheckUniqueFilenames.hook(cov, '_should_trace') CheckUniqueFilenames.hook(cov, '_check_include_omit_etc') cov.set_option("run:plugins", ["tests.plugin1"]) # Import the Python file, executing it. self.start_import_stop(cov, "simple") _, statements, missing, _ = cov.analysis("simple.py") self.assertEqual(statements, [1, 2, 3]) self.assertEqual(missing, []) zzfile = os.path.abspath(os.path.join("/src", "try_ABC.zz")) _, statements, _, _ = cov.analysis(zzfile) self.assertEqual(statements, [105, 106, 107, 205, 206, 207]) def make_render_and_caller(self): """Make the render.py and caller.py files we need.""" # plugin2 emulates a dynamic tracing plugin: the caller's locals # are examined to determine the source file and line number. # The plugin is in tests/plugin2.py. self.make_file("render.py", """\ def render(filename, linenum): # This function emulates a template renderer. The plugin # will examine the `filename` and `linenum` locals to # determine the source file and line number. fiddle_around = 1 # not used, just chaff. return "[{0} @ {1}]".format(filename, linenum) def helper(x): # This function is here just to show that not all code in # this file will be part of the dynamic tracing. return x+1 """) self.make_file("caller.py", """\ import sys from render import helper, render assert render("foo_7.html", 4) == "[foo_7.html @ 4]" # Render foo_7.html again to try the CheckUniqueFilenames asserts. render("foo_7.html", 4) assert helper(42) == 43 assert render("bar_4.html", 2) == "[bar_4.html @ 2]" assert helper(76) == 77 # quux_5.html will be omitted from the results. assert render("quux_5.html", 3) == "[quux_5.html @ 3]" # For Python 2, make sure unicode is working. assert render(u"uni_3.html", 2) == "[uni_3.html @ 2]" """) # will try to read the actual source files, so make some # source files. def lines(n): """Make a string with n lines of text.""" return "".join("line %d\n" % i for i in range(n)) self.make_file("bar_4.html", lines(4)) self.make_file("foo_7.html", lines(7)) def test_plugin2(self): self.make_render_and_caller() cov = coverage.Coverage(omit=["*quux*"]) CheckUniqueFilenames.hook(cov, '_should_trace') CheckUniqueFilenames.hook(cov, '_check_include_omit_etc') cov.set_option("run:plugins", ["tests.plugin2"]) self.start_import_stop(cov, "caller") # The way plugin2 works, a file named foo_7.html will be claimed to # have 7 lines in it. If render() was called with line number 4, # then the plugin will claim that lines 4 and 5 were executed. _, statements, missing, _ = cov.analysis("foo_7.html") self.assertEqual(statements, [1, 2, 3, 4, 5, 6, 7]) self.assertEqual(missing, [1, 2, 3, 6, 7]) self.assertIn("foo_7.html", cov.data.line_counts()) _, statements, missing, _ = cov.analysis("bar_4.html") self.assertEqual(statements, [1, 2, 3, 4]) self.assertEqual(missing, [1, 4]) self.assertIn("bar_4.html", cov.data.line_counts()) self.assertNotIn("quux_5.html", cov.data.line_counts()) _, statements, missing, _ = cov.analysis("uni_3.html") self.assertEqual(statements, [1, 2, 3]) self.assertEqual(missing, [1]) self.assertIn("uni_3.html", cov.data.line_counts()) def test_plugin2_with_branch(self): self.make_render_and_caller() cov = coverage.Coverage(branch=True, omit=["*quux*"]) CheckUniqueFilenames.hook(cov, '_should_trace') CheckUniqueFilenames.hook(cov, '_check_include_omit_etc') cov.set_option("run:plugins", ["tests.plugin2"]) self.start_import_stop(cov, "caller") # The way plugin2 works, a file named foo_7.html will be claimed to # have 7 lines in it. If render() was called with line number 4, # then the plugin will claim that lines 4 and 5 were executed. analysis = cov._analyze("foo_7.html") self.assertEqual(analysis.statements, set([1, 2, 3, 4, 5, 6, 7])) # Plugins don't do branch coverage yet. self.assertEqual(analysis.has_arcs(), True) self.assertEqual(analysis.arc_possibilities(), []) self.assertEqual(analysis.missing, set([1, 2, 3, 6, 7])) def test_plugin2_with_text_report(self): self.make_render_and_caller() cov = coverage.Coverage(branch=True, omit=["*quux*"]) cov.set_option("run:plugins", ["tests.plugin2"]) self.start_import_stop(cov, "caller") repout = StringIO() total = cov.report(file=repout, include=["*.html"], omit=["uni*.html"], show_missing=True) report = repout.getvalue().splitlines() expected = [ 'Name Stmts Miss Branch BrPart Cover Missing', '--------------------------------------------------------', 'bar_4.html 4 2 0 0 50% 1, 4', 'foo_7.html 7 5 0 0 29% 1-3, 6-7', '--------------------------------------------------------', 'TOTAL 11 7 0 0 36%', ] self.assertEqual(report, expected) self.assertAlmostEqual(total, 36.36, places=2) def test_plugin2_with_html_report(self): self.make_render_and_caller() cov = coverage.Coverage(branch=True, omit=["*quux*"]) cov.set_option("run:plugins", ["tests.plugin2"]) self.start_import_stop(cov, "caller") total = cov.html_report(include=["*.html"], omit=["uni*.html"]) self.assertAlmostEqual(total, 36.36, places=2) self.assert_exists("htmlcov/index.html") self.assert_exists("htmlcov/bar_4_html.html") self.assert_exists("htmlcov/foo_7_html.html") def test_plugin2_with_xml_report(self): self.make_render_and_caller() cov = coverage.Coverage(branch=True, omit=["*quux*"]) cov.set_option("run:plugins", ["tests.plugin2"]) self.start_import_stop(cov, "caller") total = cov.xml_report(include=["*.html"], omit=["uni*.html"]) self.assertAlmostEqual(total, 36.36, places=2) with open("coverage.xml") as fxml: xml = fxml.read() for snip in [ 'filename="bar_4.html" line-rate="0.5" name="bar_4.html"', 'filename="foo_7.html" line-rate="0.2857" name="foo_7.html"', ]: self.assertIn(snip, xml) def test_defer_to_python(self): # A plugin that measures, but then wants built-in python reporting. self.make_file("fairly_odd_plugin.py", """\ # A plugin that claims all the odd lines are executed, and none of # the even lines, and then punts reporting off to the built-in # Python reporting. import coverage.plugin class Plugin(coverage.CoveragePlugin): def file_tracer(self, filename): return OddTracer(filename) def file_reporter(self, filename): return "python" class OddTracer(coverage.plugin.FileTracer): def __init__(self, filename): self.filename = filename def source_filename(self): return self.filename def line_number_range(self, frame): lineno = frame.f_lineno if lineno % 2: return (lineno, lineno) else: return (-1, -1) def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.make_file("unsuspecting.py", """\ a = 1 b = 2 c = 3 d = 4 e = 5 f = 6 """) cov = coverage.Coverage(include=["unsuspecting.py"]) cov.set_option("run:plugins", ["fairly_odd_plugin"]) self.start_import_stop(cov, "unsuspecting") repout = StringIO() total = cov.report(file=repout, show_missing=True) report = repout.getvalue().splitlines() expected = [ 'Name Stmts Miss Cover Missing', '-----------------------------------------------', 'unsuspecting.py 6 3 50% 2, 4, 6', ] self.assertEqual(report, expected) self.assertEqual(total, 50) def test_find_unexecuted(self): self.make_file("unexecuted_plugin.py", """\ import os import coverage.plugin class Plugin(coverage.CoveragePlugin): def file_tracer(self, filename): if filename.endswith("foo.py"): return MyTracer(filename) def file_reporter(self, filename): return MyReporter(filename) def find_executable_files(self, src_dir): # Check that src_dir is the right value files = os.listdir(src_dir) assert "foo.py" in files assert "unexecuted_plugin.py" in files return ["chimera.py"] class MyTracer(coverage.plugin.FileTracer): def __init__(self, filename): self.filename = filename def source_filename(self): return self.filename def line_number_range(self, frame): return (999, 999) class MyReporter(coverage.FileReporter): def lines(self): return set([99, 999, 9999]) def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.make_file("foo.py", "a = 1\n") cov = coverage.Coverage(source=['.']) cov.set_option("run:plugins", ["unexecuted_plugin"]) self.start_import_stop(cov, "foo") # The file we executed claims to have run line 999. _, statements, missing, _ = cov.analysis("foo.py") self.assertEqual(statements, [99, 999, 9999]) self.assertEqual(missing, [99, 9999]) # The completely missing file is in the results. _, statements, missing, _ = cov.analysis("chimera.py") self.assertEqual(statements, [99, 999, 9999]) self.assertEqual(missing, [99, 999, 9999]) # But completely new filenames are not in the results. self.assertEqual(len(cov.get_data().measured_files()), 3) with self.assertRaises(CoverageException): cov.analysis("fictional.py") class BadFileTracerTest(FileTracerTest): """Test error handling around file tracer plugins.""" def run_plugin(self, module_name): """Run a plugin with the given module_name. Uses a few fixed Python files. Returns the Coverage object. """ self.make_file("simple.py", """\ import other, another a = other.f(2) b = other.f(3) c = another.g(4) d = another.g(5) """) # The names of these files are important: some plugins apply themselves # to "*other.py". self.make_file("other.py", """\ def f(x): return x+1 """) self.make_file("another.py", """\ def g(x): return x-1 """) cov = coverage.Coverage() cov.set_option("run:plugins", [module_name]) self.start_import_stop(cov, "simple") return cov def run_bad_plugin(self, module_name, plugin_name, our_error=True, excmsg=None, excmsgs=None): """Run a file, and see that the plugin failed. `module_name` and `plugin_name` is the module and name of the plugin to use. `our_error` is True if the error reported to the user will be an explicit error in our test code, marked with an '# Oh noes!' comment. `excmsg`, if provided, is text that must appear in the stderr. `excmsgs`, if provided, is a list of messages, one of which must appear in the stderr. The plugin will be disabled, and we check that a warning is output explaining why. """ self.run_plugin(module_name) stderr = self.stderr() if our_error: errors = stderr.count("# Oh noes!") # The exception we're causing should only appear once. self.assertEqual(errors, 1) # There should be a warning explaining what's happening, but only one. # The message can be in two forms: # Disabling plug-in '...' due to previous exception # or: # Disabling plug-in '...' due to an exception: msg = "Disabling plug-in '%s.%s' due to " % (module_name, plugin_name) warnings = stderr.count(msg) self.assertEqual(warnings, 1) if excmsg: self.assertIn(excmsg, stderr) if excmsgs: self.assertTrue(any(em in stderr for em in excmsgs), "expected one of %r" % excmsgs) def test_file_tracer_has_no_file_tracer_method(self): self.make_file("bad_plugin.py", """\ class Plugin(object): pass def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.run_bad_plugin("bad_plugin", "Plugin", our_error=False) def test_file_tracer_has_inherited_sourcefilename_method(self): self.make_file("bad_plugin.py", """\ import coverage class Plugin(coverage.CoveragePlugin): def file_tracer(self, filename): # Just grab everything. return FileTracer() class FileTracer(coverage.FileTracer): pass def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.run_bad_plugin( "bad_plugin", "Plugin", our_error=False, excmsg="Class 'bad_plugin.FileTracer' needs to implement source_filename()", ) def test_plugin_has_inherited_filereporter_method(self): self.make_file("bad_plugin.py", """\ import coverage class Plugin(coverage.CoveragePlugin): def file_tracer(self, filename): # Just grab everything. return FileTracer() class FileTracer(coverage.FileTracer): def source_filename(self): return "foo.xxx" def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) cov = self.run_plugin("bad_plugin") expected_msg = "Plugin 'bad_plugin.Plugin' needs to implement file_reporter()" with self.assertRaisesRegex(NotImplementedError, expected_msg): cov.report() def test_file_tracer_fails(self): self.make_file("bad_plugin.py", """\ import coverage.plugin class Plugin(coverage.plugin.CoveragePlugin): def file_tracer(self, filename): 17/0 # Oh noes! def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.run_bad_plugin("bad_plugin", "Plugin") def test_file_tracer_returns_wrong(self): self.make_file("bad_plugin.py", """\ import coverage.plugin class Plugin(coverage.plugin.CoveragePlugin): def file_tracer(self, filename): return 3.14159 def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.run_bad_plugin( "bad_plugin", "Plugin", our_error=False, excmsg="'float' object has no attribute", ) def test_has_dynamic_source_filename_fails(self): self.make_file("bad_plugin.py", """\ import coverage.plugin class Plugin(coverage.plugin.CoveragePlugin): def file_tracer(self, filename): return BadFileTracer() class BadFileTracer(coverage.plugin.FileTracer): def has_dynamic_source_filename(self): 23/0 # Oh noes! def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.run_bad_plugin("bad_plugin", "Plugin") def test_source_filename_fails(self): self.make_file("bad_plugin.py", """\ import coverage.plugin class Plugin(coverage.plugin.CoveragePlugin): def file_tracer(self, filename): return BadFileTracer() class BadFileTracer(coverage.plugin.FileTracer): def source_filename(self): 42/0 # Oh noes! def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.run_bad_plugin("bad_plugin", "Plugin") def test_source_filename_returns_wrong(self): self.make_file("bad_plugin.py", """\ import coverage.plugin class Plugin(coverage.plugin.CoveragePlugin): def file_tracer(self, filename): return BadFileTracer() class BadFileTracer(coverage.plugin.FileTracer): def source_filename(self): return 17.3 def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.run_bad_plugin( "bad_plugin", "Plugin", our_error=False, excmsgs=[ "expected str, bytes or os.PathLike object, not float", "'float' object has no attribute", "object of type 'float' has no len()", "'float' object is unsubscriptable", ], ) def test_dynamic_source_filename_fails(self): self.make_file("bad_plugin.py", """\ import coverage.plugin class Plugin(coverage.plugin.CoveragePlugin): def file_tracer(self, filename): if filename.endswith("other.py"): return BadFileTracer() class BadFileTracer(coverage.plugin.FileTracer): def has_dynamic_source_filename(self): return True def dynamic_source_filename(self, filename, frame): 101/0 # Oh noes! def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.run_bad_plugin("bad_plugin", "Plugin") def test_line_number_range_returns_non_tuple(self): self.make_file("bad_plugin.py", """\ import coverage.plugin class Plugin(coverage.plugin.CoveragePlugin): def file_tracer(self, filename): if filename.endswith("other.py"): return BadFileTracer() class BadFileTracer(coverage.plugin.FileTracer): def source_filename(self): return "something.foo" def line_number_range(self, frame): return 42.23 def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.run_bad_plugin( "bad_plugin", "Plugin", our_error=False, excmsg="line_number_range must return 2-tuple", ) def test_line_number_range_returns_triple(self): self.make_file("bad_plugin.py", """\ import coverage.plugin class Plugin(coverage.plugin.CoveragePlugin): def file_tracer(self, filename): if filename.endswith("other.py"): return BadFileTracer() class BadFileTracer(coverage.plugin.FileTracer): def source_filename(self): return "something.foo" def line_number_range(self, frame): return (1, 2, 3) def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.run_bad_plugin( "bad_plugin", "Plugin", our_error=False, excmsg="line_number_range must return 2-tuple", ) def test_line_number_range_returns_pair_of_strings(self): self.make_file("bad_plugin.py", """\ import coverage.plugin class Plugin(coverage.plugin.CoveragePlugin): def file_tracer(self, filename): if filename.endswith("other.py"): return BadFileTracer() class BadFileTracer(coverage.plugin.FileTracer): def source_filename(self): return "something.foo" def line_number_range(self, frame): return ("5", "7") def coverage_init(reg, options): reg.add_file_tracer(Plugin()) """) self.run_bad_plugin( "bad_plugin", "Plugin", our_error=False, excmsg="an integer is required", ) class ConfigurerPluginTest(CoverageTest): """Test configuring plugins.""" run_in_temp_dir = False def test_configurer_plugin(self): cov = coverage.Coverage() cov.set_option("run:plugins", ["tests.plugin_config"]) cov.start() cov.stop() # pragma: nested excluded = cov.get_option("report:exclude_lines") self.assertIn("pragma: custom", excluded) self.assertIn("pragma: or whatever", excluded) python-coverage-4.5+dfsg.1.orig/tests/osinfo.py0000644000076600000620000000524213173515667017510 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """OS information for testing.""" from coverage import env if env.WINDOWS: # Windows implementation def process_ram(): """How much RAM is this process using? (Windows)""" import ctypes # From: http://lists.ubuntu.com/archives/bazaar-commits/2009-February/011990.html class PROCESS_MEMORY_COUNTERS_EX(ctypes.Structure): """Used by GetProcessMemoryInfo""" _fields_ = [ ('cb', ctypes.c_ulong), ('PageFaultCount', ctypes.c_ulong), ('PeakWorkingSetSize', ctypes.c_size_t), ('WorkingSetSize', ctypes.c_size_t), ('QuotaPeakPagedPoolUsage', ctypes.c_size_t), ('QuotaPagedPoolUsage', ctypes.c_size_t), ('QuotaPeakNonPagedPoolUsage', ctypes.c_size_t), ('QuotaNonPagedPoolUsage', ctypes.c_size_t), ('PagefileUsage', ctypes.c_size_t), ('PeakPagefileUsage', ctypes.c_size_t), ('PrivateUsage', ctypes.c_size_t), ] mem_struct = PROCESS_MEMORY_COUNTERS_EX() ret = ctypes.windll.psapi.GetProcessMemoryInfo( ctypes.windll.kernel32.GetCurrentProcess(), ctypes.byref(mem_struct), ctypes.sizeof(mem_struct) ) if not ret: # pragma: part covered return 0 # pragma: cant happen return mem_struct.PrivateUsage elif env.LINUX: # Linux implementation import os _scale = {'kb': 1024, 'mb': 1024*1024} def _VmB(key): """Read the /proc/PID/status file to find memory use.""" try: # Get pseudo file /proc//status with open('/proc/%d/status' % os.getpid()) as t: v = t.read() except IOError: # pragma: cant happen return 0 # non-Linux? # Get VmKey line e.g. 'VmRSS: 9999 kB\n ...' i = v.index(key) v = v[i:].split(None, 3) if len(v) < 3: # pragma: part covered return 0 # pragma: cant happen # Convert Vm value to bytes. return int(float(v[1]) * _scale[v[2].lower()]) def process_ram(): """How much RAM is this process using? (Linux implementation)""" return _VmB('VmRSS') else: # Generic implementation. def process_ram(): """How much RAM is this process using? (stdlib implementation)""" import resource return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss python-coverage-4.5+dfsg.1.orig/tests/qunit/0000755000076600000620000000000013237000174016755 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/test_parser.py0000644000076600000620000003302613177624110020533 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for coverage.py's code parsing.""" import textwrap from tests.coveragetest import CoverageTest from coverage import env from coverage.misc import NotPython from coverage.parser import PythonParser class PythonParserTest(CoverageTest): """Tests for coverage.py's Python code parsing.""" run_in_temp_dir = False def parse_source(self, text): """Parse `text` as source, and return the `PythonParser` used.""" if env.PY2: text = text.decode("ascii") text = textwrap.dedent(text) parser = PythonParser(text=text, exclude="nocover") parser.parse_source() return parser def test_exit_counts(self): parser = self.parse_source("""\ # check some basic branch counting class Foo: def foo(self, a): if a: return 5 else: return 7 class Bar: pass """) self.assertEqual(parser.exit_counts(), { 2:1, 3:1, 4:2, 5:1, 7:1, 9:1, 10:1 }) def test_generator_exit_counts(self): # https://bitbucket.org/ned/coveragepy/issue/324/yield-in-loop-confuses-branch-coverage parser = self.parse_source("""\ def gen(input): for n in inp: yield (i * 2 for i in range(n)) list(gen([1,2,3])) """) self.assertEqual(parser.exit_counts(), { 1:1, # def -> list 2:2, # for -> yield; for -> exit 3:2, # yield -> for; genexp exit 5:1, # list -> exit }) def test_try_except(self): parser = self.parse_source("""\ try: a = 2 except ValueError: a = 4 except ZeroDivideError: a = 6 except: a = 8 b = 9 """) self.assertEqual(parser.exit_counts(), { 1: 1, 2:1, 3:2, 4:1, 5:2, 6:1, 7:1, 8:1, 9:1 }) def test_excluded_classes(self): parser = self.parse_source("""\ class Foo: def __init__(self): pass if len([]): # nocover class Bar: pass """) self.assertEqual(parser.exit_counts(), { 1:0, 2:1, 3:1 }) def test_missing_branch_to_excluded_code(self): parser = self.parse_source("""\ if fooey: a = 2 else: # nocover a = 4 b = 5 """) self.assertEqual(parser.exit_counts(), { 1:1, 2:1, 5:1 }) parser = self.parse_source("""\ def foo(): if fooey: a = 3 else: a = 5 b = 6 """) self.assertEqual(parser.exit_counts(), { 1:1, 2:2, 3:1, 5:1, 6:1 }) parser = self.parse_source("""\ def foo(): if fooey: a = 3 else: # nocover a = 5 b = 6 """) self.assertEqual(parser.exit_counts(), { 1:1, 2:1, 3:1, 6:1 }) def test_indentation_error(self): msg = ( "Couldn't parse '' as Python source: " "'unindent does not match any outer indentation level' at line 3" ) with self.assertRaisesRegex(NotPython, msg): _ = self.parse_source("""\ 0 spaces 2 1 """) def test_token_error(self): msg = "Couldn't parse '' as Python source: 'EOF in multi-line string' at line 1" with self.assertRaisesRegex(NotPython, msg): _ = self.parse_source("""\ ''' """) def test_decorator_pragmas(self): parser = self.parse_source("""\ # 1 @foo(3) # nocover @bar def func(x, y=5): return 6 class Foo: # this is the only statement. '''9''' @foo # nocover def __init__(self): '''12''' return 13 @foo( # nocover 16, 17, ) def meth(self): return 20 @foo( # nocover 23 ) def func(x=25): return 26 """) if env.PYVERSION < (3, 7): raw_statements = set([3, 4, 5, 6, 8, 9, 10, 13, 15, 16, 17, 20, 22, 23, 25, 26]) else: # Python 3.7 no longer includes class docstrings in the lnotab table. raw_statements = set([3, 4, 5, 6, 8, 10, 13, 15, 16, 17, 20, 22, 23, 25, 26]) self.assertEqual(parser.raw_statements, raw_statements) self.assertEqual(parser.statements, set([8])) def test_class_decorator_pragmas(self): parser = self.parse_source("""\ class Foo(object): def __init__(self): self.x = 3 @foo # nocover class Bar(object): def __init__(self): self.x = 8 """) self.assertEqual(parser.raw_statements, set([1, 2, 3, 5, 6, 7, 8])) self.assertEqual(parser.statements, set([1, 2, 3])) class ParserMissingArcDescriptionTest(CoverageTest): """Tests for PythonParser.missing_arc_description.""" run_in_temp_dir = False def parse_text(self, source): """Parse Python source, and return the parser object.""" parser = PythonParser(text=textwrap.dedent(source)) parser.parse_source() return parser def test_missing_arc_description(self): # This code is never run, so the actual values don't matter. parser = self.parse_text(u"""\ if x: print(2) print(3) def func5(): for x in range(6): if x == 7: break def func10(): while something(11): thing(12) more_stuff(13) """) self.assertEqual( parser.missing_arc_description(1, 2), "line 1 didn't jump to line 2, because the condition on line 1 was never true" ) self.assertEqual( parser.missing_arc_description(1, 3), "line 1 didn't jump to line 3, because the condition on line 1 was never false" ) self.assertEqual( parser.missing_arc_description(6, -5), "line 6 didn't return from function 'func5', " "because the loop on line 6 didn't complete" ) self.assertEqual( parser.missing_arc_description(6, 7), "line 6 didn't jump to line 7, because the loop on line 6 never started" ) self.assertEqual( parser.missing_arc_description(11, 12), "line 11 didn't jump to line 12, because the condition on line 11 was never true" ) self.assertEqual( parser.missing_arc_description(11, 13), "line 11 didn't jump to line 13, because the condition on line 11 was never false" ) def test_missing_arc_descriptions_for_small_callables(self): # We use 2.7 features here, so just skip this test on 2.6 if env.PYVERSION < (2, 7): self.skipTest("No dict or set comps in 2.6") parser = self.parse_text(u"""\ callables = [ lambda: 2, (x for x in range(3)), {x:1 for x in range(4)}, {x for x in range(5)}, ] x = 7 """) self.assertEqual( parser.missing_arc_description(2, -2), "line 2 didn't finish the lambda on line 2" ) self.assertEqual( parser.missing_arc_description(3, -3), "line 3 didn't finish the generator expression on line 3" ) self.assertEqual( parser.missing_arc_description(4, -4), "line 4 didn't finish the dictionary comprehension on line 4" ) self.assertEqual( parser.missing_arc_description(5, -5), "line 5 didn't finish the set comprehension on line 5" ) def test_missing_arc_descriptions_for_exceptions(self): parser = self.parse_text(u"""\ try: pass except ZeroDivideError: print("whoops") except ValueError: print("yikes") """) self.assertEqual( parser.missing_arc_description(3, 4), "line 3 didn't jump to line 4, because the exception caught by line 3 didn't happen" ) self.assertEqual( parser.missing_arc_description(5, 6), "line 5 didn't jump to line 6, because the exception caught by line 5 didn't happen" ) def test_missing_arc_descriptions_for_finally(self): parser = self.parse_text(u"""\ def function(): for i in range(2): try: if something(4): break else: if something(7): continue else: continue if also_this(11): return 12 else: raise Exception(14) finally: this_thing(16) that_thing(17) """) self.assertEqual( parser.missing_arc_description(16, 17), "line 16 didn't jump to line 17, because the break on line 5 wasn't executed" ) self.assertEqual( parser.missing_arc_description(16, 2), "line 16 didn't jump to line 2, " "because the continue on line 8 wasn't executed" " or " "the continue on line 10 wasn't executed" ) self.assertEqual( parser.missing_arc_description(16, -1), "line 16 didn't except from function 'function', " "because the raise on line 14 wasn't executed" " or " "line 16 didn't return from function 'function', " "because the return on line 12 wasn't executed" ) def test_missing_arc_descriptions_bug460(self): parser = self.parse_text(u"""\ x = 1 d = { 3: lambda: [], 4: lambda: [], } x = 6 """) self.assertEqual( parser.missing_arc_description(2, -3), "line 3 didn't finish the lambda on line 3", ) class ParserFileTest(CoverageTest): """Tests for coverage.py's code parsing from files.""" def parse_file(self, filename): """Parse `text` as source, and return the `PythonParser` used.""" parser = PythonParser(filename=filename, exclude="nocover") parser.parse_source() return parser def test_line_endings(self): text = """\ # check some basic branch counting class Foo: def foo(self, a): if a: return 5 else: return 7 class Bar: pass """ counts = { 2:1, 3:1, 4:2, 5:1, 7:1, 9:1, 10:1 } name_endings = (("unix", "\n"), ("dos", "\r\n"), ("mac", "\r")) for fname, newline in name_endings: fname = fname + ".py" self.make_file(fname, text, newline=newline) parser = self.parse_file(fname) self.assertEqual( parser.exit_counts(), counts, "Wrong for %r" % fname ) def test_encoding(self): self.make_file("encoded.py", """\ coverage = "\xe7\xf6v\xear\xe3g\xe9" """) parser = self.parse_file("encoded.py") self.assertEqual(parser.exit_counts(), {1: 1}) def test_missing_line_ending(self): # Test that the set of statements is the same even if a final # multi-line statement has no final newline. # https://bitbucket.org/ned/coveragepy/issue/293 self.make_file("normal.py", """\ out, err = subprocess.Popen( [sys.executable, '-c', 'pass'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() """) parser = self.parse_file("normal.py") self.assertEqual(parser.statements, set([1])) self.make_file("abrupt.py", """\ out, err = subprocess.Popen( [sys.executable, '-c', 'pass'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()""") # no final newline. # Double-check that some test helper wasn't being helpful. with open("abrupt.py") as f: self.assertEqual(f.read()[-1], ")") parser = self.parse_file("abrupt.py") self.assertEqual(parser.statements, set([1])) python-coverage-4.5+dfsg.1.orig/tests/test_debug.py0000644000076600000620000001577313177624110020336 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests of coverage/debug.py""" import os import pytest import coverage from coverage.backward import StringIO from coverage.debug import filter_text, info_formatter, info_header, short_id, short_stack from tests.coveragetest import CoverageTest from tests.helpers import re_lines class InfoFormatterTest(CoverageTest): """Tests of misc.info_formatter.""" run_in_temp_dir = False def test_info_formatter(self): lines = list(info_formatter([ ('x', 'hello there'), ('very long label', ['one element']), ('regular', ['abc', 'def', 'ghi', 'jkl']), ('nothing', []), ])) self.assertEqual(lines, [ ' x: hello there', 'very long label: one element', ' regular: abc', ' def', ' ghi', ' jkl', ' nothing: -none-', ]) def test_info_formatter_with_generator(self): lines = list(info_formatter(('info%d' % i, i) for i in range(3))) self.assertEqual(lines, ['info0: 0', 'info1: 1', 'info2: 2']) @pytest.mark.parametrize("label, header", [ ("x", "-- x ---------------------------------------------------------"), ("hello there", "-- hello there -----------------------------------------------"), ]) def test_info_header(label, header): assert info_header(label) == header @pytest.mark.parametrize("id64, id16", [ (0x1234, 0x1234), (0x12340000, 0x1234), (0xA5A55A5A, 0xFFFF), (0x1234cba956780fed, 0x8008), ]) def test_short_id(id64, id16): assert short_id(id64) == id16 @pytest.mark.parametrize("text, filters, result", [ ("hello", [], "hello"), ("hello\n", [], "hello\n"), ("hello\nhello\n", [], "hello\nhello\n"), ("hello\nbye\n", [lambda x: "="+x], "=hello\n=bye\n"), ("hello\nbye\n", [lambda x: "="+x, lambda x: x+"\ndone\n"], "=hello\ndone\n=bye\ndone\n"), ]) def test_filter_text(text, filters, result): assert filter_text(text, filters) == result class DebugTraceTest(CoverageTest): """Tests of debug output.""" def f1_debug_output(self, debug): """Runs some code with `debug` option, returns the debug output.""" # Make code to run. self.make_file("f1.py", """\ def f1(x): return x+1 for i in range(5): f1(i) """) debug_out = StringIO() cov = coverage.Coverage(debug=debug) cov._debug_file = debug_out self.start_import_stop(cov, "f1") cov.save() out_lines = debug_out.getvalue() return out_lines def test_debug_no_trace(self): out_lines = self.f1_debug_output([]) # We should have no output at all. self.assertFalse(out_lines) def test_debug_trace(self): out_lines = self.f1_debug_output(["trace"]) # We should have a line like "Tracing 'f1.py'" self.assertIn("Tracing 'f1.py'", out_lines) # We should have lines like "Not tracing 'collector.py'..." coverage_lines = re_lines( out_lines, r"^Not tracing .*: is part of coverage.py$" ) self.assertTrue(coverage_lines) def test_debug_trace_pid(self): out_lines = self.f1_debug_output(["trace", "pid"]) # Now our lines are always prefixed with the process id. pid_prefix = r"^%5d\.[0-9a-f]{4}: " % os.getpid() pid_lines = re_lines(out_lines, pid_prefix) self.assertEqual(pid_lines, out_lines) # We still have some tracing, and some not tracing. self.assertTrue(re_lines(out_lines, pid_prefix + "Tracing ")) self.assertTrue(re_lines(out_lines, pid_prefix + "Not tracing ")) def test_debug_callers(self): out_lines = self.f1_debug_output(["pid", "dataop", "dataio", "callers"]) print(out_lines) # For every real message, there should be a stack # trace with a line like "f1_debug_output : /Users/ned/coverage/tests/test_debug.py @71" real_messages = re_lines(out_lines, r" @\d+", match=False).splitlines() frame_pattern = r"\s+f1_debug_output : .*tests[/\\]test_debug.py @\d+$" frames = re_lines(out_lines, frame_pattern).splitlines() self.assertEqual(len(real_messages), len(frames)) # The last message should be "Writing data", and the last frame should # be write_file in data.py. self.assertRegex(real_messages[-1], r"^\s*\d+\.\w{4}: Writing data") last_line = out_lines.splitlines()[-1] self.assertRegex(last_line, r"\s+write_file : .*coverage[/\\]data.py @\d+$") def test_debug_config(self): out_lines = self.f1_debug_output(["config"]) labels = """ attempted_config_files branch config_files cover_pylib data_file debug exclude_list extra_css html_dir html_title ignore_errors run_include run_omit parallel partial_always_list partial_list paths precision show_missing source timid xml_output report_include report_omit """.split() for label in labels: label_pat = r"^\s*%s: " % label self.assertEqual( len(re_lines(out_lines, label_pat).splitlines()), 1 ) def test_debug_sys(self): out_lines = self.f1_debug_output(["sys"]) labels = """ version coverage cover_paths pylib_paths tracer config_files configs_read data_path python platform implementation executable cwd path environment command_line cover_match pylib_match """.split() for label in labels: label_pat = r"^\s*%s: " % label self.assertEqual( len(re_lines(out_lines, label_pat).splitlines()), 1, msg="Incorrect lines for %r" % label, ) def f_one(*args, **kwargs): """First of the chain of functions for testing `short_stack`.""" return f_two(*args, **kwargs) def f_two(*args, **kwargs): """Second of the chain of functions for testing `short_stack`.""" return f_three(*args, **kwargs) def f_three(*args, **kwargs): """Third of the chain of functions for testing `short_stack`.""" return short_stack(*args, **kwargs) class ShortStackTest(CoverageTest): """Tests of coverage.debug.short_stack.""" run_in_temp_dir = False def test_short_stack(self): stack = f_one().splitlines() self.assertGreater(len(stack), 10) self.assertIn("f_three", stack[-1]) self.assertIn("f_two", stack[-2]) self.assertIn("f_one", stack[-3]) def test_short_stack_limit(self): stack = f_one(limit=5).splitlines() self.assertEqual(len(stack), 5) def test_short_stack_skip(self): stack = f_one(skip=1).splitlines() self.assertIn("f_two", stack[-1]) python-coverage-4.5+dfsg.1.orig/tests/coveragetest.py0000644000076600000620000004673113177624110020702 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Base test case class for coverage.py testing.""" import contextlib import datetime import functools import os import random import re import shlex import sys import types from unittest_mixins import ( EnvironmentAwareMixin, StdStreamCapturingMixin, TempDirMixin, DelayedAssertionMixin, ) import coverage from coverage import env from coverage.backunittest import TestCase, unittest from coverage.backward import StringIO, import_local_file, string_class, shlex_quote from coverage.cmdline import CoverageScript from coverage.debug import _TEST_NAME_FILE from coverage.misc import StopEverything from tests.helpers import run_command, SuperModuleCleaner # Status returns for the command line. OK, ERR = 0, 1 # The coverage/tests directory, for all sorts of finding test helping things. TESTS_DIR = os.path.dirname(__file__) def convert_skip_exceptions(method): """A decorator for test methods to convert StopEverything to SkipTest.""" @functools.wraps(method) def wrapper(*args, **kwargs): """Run the test method, and convert exceptions.""" try: result = method(*args, **kwargs) except StopEverything: raise unittest.SkipTest("StopEverything!") return result return wrapper class SkipConvertingMetaclass(type): """Decorate all test methods to convert StopEverything to SkipTest.""" def __new__(mcs, name, bases, attrs): for attr_name, attr_value in attrs.items(): if attr_name.startswith('test_') and isinstance(attr_value, types.FunctionType): attrs[attr_name] = convert_skip_exceptions(attr_value) return super(SkipConvertingMetaclass, mcs).__new__(mcs, name, bases, attrs) CoverageTestMethodsMixin = SkipConvertingMetaclass('CoverageTestMethodsMixin', (), {}) class CoverageTest( EnvironmentAwareMixin, StdStreamCapturingMixin, TempDirMixin, DelayedAssertionMixin, CoverageTestMethodsMixin, TestCase, ): """A base class for coverage.py test cases.""" # Standard unittest setting: show me diffs even if they are very long. maxDiff = None # Tell newer unittest implementations to print long helpful messages. longMessage = True # Let stderr go to stderr, pytest will capture it for us. show_stderr = True def setUp(self): super(CoverageTest, self).setUp() self.module_cleaner = SuperModuleCleaner() # Attributes for getting info about what happened. self.last_command_status = None self.last_command_output = None self.last_module_name = None if _TEST_NAME_FILE: # pragma: debugging with open(_TEST_NAME_FILE, "w") as f: f.write("%s_%s" % ( self.__class__.__name__, self._testMethodName, )) def clean_local_file_imports(self): """Clean up the results of calls to `import_local_file`. Use this if you need to `import_local_file` the same file twice in one test. """ self.module_cleaner.clean_local_file_imports() def start_import_stop(self, cov, modname, modfile=None): """Start coverage, import a file, then stop coverage. `cov` is started and stopped, with an `import_local_file` of `modname` in the middle. `modfile` is the file to import as `modname` if it isn't in the current directory. The imported module is returned. """ cov.start() try: # pragma: nested # Import the Python file, executing it. mod = import_local_file(modname, modfile) finally: # pragma: nested # Stop coverage.py. cov.stop() return mod def get_module_name(self): """Return a random module name to use for this test run.""" self.last_module_name = 'coverage_test_' + str(random.random())[2:] return self.last_module_name # Map chars to numbers for arcz_to_arcs _arcz_map = {'.': -1} _arcz_map.update(dict((c, ord(c) - ord('0')) for c in '123456789')) _arcz_map.update(dict( (c, 10 + ord(c) - ord('A')) for c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' )) def arcz_to_arcs(self, arcz): """Convert a compact textual representation of arcs to a list of pairs. The text has space-separated pairs of letters. Period is -1, 1-9 are 1-9, A-Z are 10 through 36. The resulting list is sorted regardless of the order of the input pairs. ".1 12 2." --> [(-1,1), (1,2), (2,-1)] Minus signs can be included in the pairs: "-11, 12, 2-5" --> [(-1,1), (1,2), (2,-5)] """ arcs = [] for pair in arcz.split(): asgn = bsgn = 1 if len(pair) == 2: a, b = pair else: assert len(pair) == 3 if pair[0] == '-': _, a, b = pair asgn = -1 else: assert pair[1] == '-' a, _, b = pair bsgn = -1 arcs.append((asgn * self._arcz_map[a], bsgn * self._arcz_map[b])) return sorted(arcs) def assert_equal_args(self, a1, a2, msg=None): """Assert that the arc lists `a1` and `a2` are equal.""" # Make them into multi-line strings so we can see what's going wrong. s1 = "\n".join(repr(a) for a in a1) + "\n" s2 = "\n".join(repr(a) for a in a2) + "\n" self.assertMultiLineEqual(s1, s2, msg) def check_coverage( self, text, lines=None, missing="", report="", excludes=None, partials="", arcz=None, arcz_missing="", arcz_unpredicted="", arcs=None, arcs_missing=None, arcs_unpredicted=None, ): """Check the coverage measurement of `text`. The source `text` is run and measured. `lines` are the line numbers that are executable, or a list of possible line numbers, any of which could match. `missing` are the lines not executed, `excludes` are regexes to match against for excluding lines, and `report` is the text of the measurement report. For arc measurement, `arcz` is a string that can be decoded into arcs in the code (see `arcz_to_arcs` for the encoding scheme). `arcz_missing` are the arcs that are not executed, and `arcz_unpredicted` are the arcs executed in the code, but not deducible from the code. These last two default to "", meaning we explicitly check that there are no missing or unpredicted arcs. Returns the Coverage object, in case you want to poke at it some more. """ # We write the code into a file so that we can import it. # Coverage.py wants to deal with things as modules with file names. modname = self.get_module_name() self.make_file(modname + ".py", text) if arcs is None and arcz is not None: arcs = self.arcz_to_arcs(arcz) if arcs_missing is None: arcs_missing = self.arcz_to_arcs(arcz_missing) if arcs_unpredicted is None: arcs_unpredicted = self.arcz_to_arcs(arcz_unpredicted) # Start up coverage.py. cov = coverage.Coverage(branch=True) cov.erase() for exc in excludes or []: cov.exclude(exc) for par in partials or []: cov.exclude(par, which='partial') mod = self.start_import_stop(cov, modname) # Clean up our side effects del sys.modules[modname] # Get the analysis results, and check that they are right. analysis = cov._analyze(mod) statements = sorted(analysis.statements) if lines is not None: if isinstance(lines[0], int): # lines is just a list of numbers, it must match the statements # found in the code. self.assertEqual(statements, lines) else: # lines is a list of possible line number lists, one of them # must match. for line_list in lines: if statements == line_list: break else: self.fail("None of the lines choices matched %r" % statements) missing_formatted = analysis.missing_formatted() if isinstance(missing, string_class): self.assertEqual(missing_formatted, missing) else: for missing_list in missing: if missing_formatted == missing_list: break else: self.fail("None of the missing choices matched %r" % missing_formatted) if arcs is not None: with self.delayed_assertions(): self.assert_equal_args( analysis.arc_possibilities(), arcs, "Possible arcs differ: minus is actual, plus is expected" ) self.assert_equal_args( analysis.arcs_missing(), arcs_missing, "Missing arcs differ: minus is actual, plus is expected" ) self.assert_equal_args( analysis.arcs_unpredicted(), arcs_unpredicted, "Unpredicted arcs differ: minus is actual, plus is expected" ) if report: frep = StringIO() cov.report(mod, file=frep, show_missing=True) rep = " ".join(frep.getvalue().split("\n")[2].split()[1:]) self.assertEqual(report, rep) return cov @contextlib.contextmanager def assert_warnings(self, cov, warnings, not_warnings=()): """A context manager to check that particular warnings happened in `cov`. `cov` is a Coverage instance. `warnings` is a list of regexes. Every regex must match a warning that was issued by `cov`. It is OK for extra warnings to be issued by `cov` that are not matched by any regex. Warnings that are disabled are still considered issued by this function. `not_warnings` is a list of regexes that must not appear in the warnings. This is only checked if there are some positive warnings to test for in `warnings`. If `warnings` is empty, then `cov` is not allowed to issue any warnings. """ saved_warnings = [] def capture_warning(msg, slug=None): """A fake implementation of Coverage._warn, to capture warnings.""" if slug: msg = "%s (%s)" % (msg, slug) saved_warnings.append(msg) original_warn = cov._warn cov._warn = capture_warning try: yield except: raise else: if warnings: for warning_regex in warnings: for saved in saved_warnings: if re.search(warning_regex, saved): break else: self.fail("Didn't find warning %r in %r" % (warning_regex, saved_warnings)) for warning_regex in not_warnings: for saved in saved_warnings: if re.search(warning_regex, saved): self.fail("Found warning %r in %r" % (warning_regex, saved_warnings)) else: # No warnings expected. Raise if any warnings happened. if saved_warnings: self.fail("Unexpected warnings: %r" % (saved_warnings,)) finally: cov._warn = original_warn def nice_file(self, *fparts): """Canonicalize the file name composed of the parts in `fparts`.""" fname = os.path.join(*fparts) return os.path.normcase(os.path.abspath(os.path.realpath(fname))) def assert_same_files(self, flist1, flist2): """Assert that `flist1` and `flist2` are the same set of file names.""" flist1_nice = [self.nice_file(f) for f in flist1] flist2_nice = [self.nice_file(f) for f in flist2] self.assertCountEqual(flist1_nice, flist2_nice) def assert_exists(self, fname): """Assert that `fname` is a file that exists.""" msg = "File %r should exist" % fname self.assertTrue(os.path.exists(fname), msg) def assert_doesnt_exist(self, fname): """Assert that `fname` is a file that doesn't exist.""" msg = "File %r shouldn't exist" % fname self.assertTrue(not os.path.exists(fname), msg) def assert_starts_with(self, s, prefix, msg=None): """Assert that `s` starts with `prefix`.""" if not s.startswith(prefix): self.fail(msg or ("%r doesn't start with %r" % (s, prefix))) def assert_recent_datetime(self, dt, seconds=10, msg=None): """Assert that `dt` marks a time at most `seconds` seconds ago.""" age = datetime.datetime.now() - dt # Python2.6 doesn't have total_seconds :( self.assertEqual(age.days, 0, msg) self.assertGreaterEqual(age.seconds, 0, msg) self.assertLessEqual(age.seconds, seconds, msg) def command_line(self, args, ret=OK, _covpkg=None): """Run `args` through the command line. Use this when you want to run the full coverage machinery, but in the current process. Exceptions may be thrown from deep in the code. Asserts that `ret` is returned by `CoverageScript.command_line`. Compare with `run_command`. Returns None. """ ret_actual = command_line(args, _covpkg=_covpkg) self.assertEqual(ret_actual, ret) coverage_command = "coverage" def run_command(self, cmd): """Run the command-line `cmd` in a sub-process. `cmd` is the command line to invoke in a sub-process. Returns the combined content of `stdout` and `stderr` output streams from the sub-process. See `run_command_status` for complete semantics. Use this when you need to test the process behavior of coverage. Compare with `command_line`. """ _, output = self.run_command_status(cmd) return output def run_command_status(self, cmd): """Run the command-line `cmd` in a sub-process, and print its output. Use this when you need to test the process behavior of coverage. Compare with `command_line`. Handles the following command names specially: * "python" is replaced with the command name of the current Python interpreter. * "coverage" is replaced with the command name for the main Coverage.py program. Returns a pair: the process' exit status and its stdout/stderr text, which are also stored as `self.last_command_status` and `self.last_command_output`. """ # Make sure "python" and "coverage" mean specifically what we want # them to mean. split_commandline = cmd.split() command_name = split_commandline[0] command_args = split_commandline[1:] if command_name == "python": # Running a Python interpreter in a sub-processes can be tricky. # Use the real name of our own executable. So "python foo.py" might # get executed as "python3.3 foo.py". This is important because # Python 3.x doesn't install as "python", so you might get a Python # 2 executable instead if you don't use the executable's basename. command_words = [os.path.basename(sys.executable)] elif command_name == "coverage": if env.JYTHON: # pragma: only jython # Jython can't do reporting, so let's skip the test now. if command_args and command_args[0] in ('report', 'html', 'xml', 'annotate'): self.skipTest("Can't run reporting commands in Jython") # Jython can't run "coverage" as a command because the shebang # refers to another shebang'd Python script. So run them as # modules. command_words = "jython -m coverage".split() else: # The invocation requests the Coverage.py program. Substitute the # actual Coverage.py main command name. command_words = [self.coverage_command] else: command_words = [command_name] cmd = " ".join([shlex_quote(w) for w in command_words] + command_args) # Add our test modules directory to PYTHONPATH. I'm sure there's too # much path munging here, but... pythonpath_name = "PYTHONPATH" if env.JYTHON: pythonpath_name = "JYTHONPATH" # pragma: only jython testmods = self.nice_file(self.working_root(), 'tests/modules') zipfile = self.nice_file(self.working_root(), 'tests/zipmods.zip') pypath = os.getenv(pythonpath_name, '') if pypath: pypath += os.pathsep pypath += testmods + os.pathsep + zipfile self.set_environ(pythonpath_name, pypath) # There are environment variables that we set when we are running the # coverage test suite under coverage. We don't want these environment # variables to leak into subprocesses we start for a test. Delete them # before running the subprocess command. self.del_environ("COVERAGE_COVERAGE") self.del_environ("COVERAGE_PROCESS_START") self.last_command_status, self.last_command_output = run_command(cmd) print(self.last_command_output) return self.last_command_status, self.last_command_output def working_root(self): """Where is the root of the coverage.py working tree?""" return os.path.dirname(self.nice_file(coverage.__file__, "..")) def report_from_command(self, cmd): """Return the report from the `cmd`, with some convenience added.""" report = self.run_command(cmd).replace('\\', '/') self.assertNotIn("error", report.lower()) return report def report_lines(self, report): """Return the lines of the report, as a list.""" lines = report.split('\n') self.assertEqual(lines[-1], "") return lines[:-1] def line_count(self, report): """How many lines are in `report`?""" return len(self.report_lines(report)) def squeezed_lines(self, report): """Return a list of the lines in report, with the spaces squeezed.""" lines = self.report_lines(report) return [re.sub(r"\s+", " ", l.strip()) for l in lines] def last_line_squeezed(self, report): """Return the last line of `report` with the spaces squeezed down.""" return self.squeezed_lines(report)[-1] class UsingModulesMixin(object): """A mixin for importing modules from tests/modules and tests/moremodules.""" def setUp(self): super(UsingModulesMixin, self).setUp() # Parent class saves and restores sys.path, we can just modify it. sys.path.append(self.nice_file(TESTS_DIR, 'modules')) sys.path.append(self.nice_file(TESTS_DIR, 'moremodules')) def command_line(args, **kwargs): """Run `args` through the CoverageScript command line. `kwargs` are the keyword arguments to the CoverageScript constructor. Returns the return code from CoverageScript.command_line. """ script = CoverageScript(**kwargs) ret = script.command_line(shlex.split(args)) return ret python-coverage-4.5+dfsg.1.orig/tests/test_version.py0000644000076600000620000000273013173515667020736 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests of version.py.""" import coverage from coverage.version import _make_url, _make_version from tests.coveragetest import CoverageTest class VersionTest(CoverageTest): """Tests of version.py""" run_in_temp_dir = False def test_version_info(self): # Make sure we didn't screw up the version_info tuple. self.assertIsInstance(coverage.version_info, tuple) self.assertEqual([type(d) for d in coverage.version_info], [int, int, int, str, int]) self.assertIn(coverage.version_info[3], ['alpha', 'beta', 'candidate', 'final']) def test_make_version(self): self.assertEqual(_make_version(4, 0, 0, 'alpha', 0), "4.0a0") self.assertEqual(_make_version(4, 0, 0, 'alpha', 1), "4.0a1") self.assertEqual(_make_version(4, 0, 0, 'final', 0), "4.0") self.assertEqual(_make_version(4, 1, 2, 'beta', 3), "4.1.2b3") self.assertEqual(_make_version(4, 1, 2, 'final', 0), "4.1.2") self.assertEqual(_make_version(5, 10, 2, 'candidate', 7), "5.10.2rc7") def test_make_url(self): self.assertEqual( _make_url(4, 0, 0, 'final', 0), "https://coverage.readthedocs.io" ) self.assertEqual( _make_url(4, 1, 2, 'beta', 3), "https://coverage.readthedocs.io/en/coverage-4.1.2b3" ) python-coverage-4.5+dfsg.1.orig/tests/plugin1.py0000644000076600000620000000305113220614734017552 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """A file tracer plugin for test_plugins.py to import.""" import os.path import coverage class Plugin(coverage.CoveragePlugin): """A file tracer plugin to import, so that it isn't in the test's current directory.""" def file_tracer(self, filename): """Trace only files named xyz.py""" if "xyz.py" in filename: return FileTracer(filename) return None def file_reporter(self, filename): return FileReporter(filename) class FileTracer(coverage.FileTracer): """A FileTracer emulating a simple static plugin.""" def __init__(self, filename): """Claim that */*xyz.py was actually sourced from /src/*ABC.zz""" self._filename = filename self._source_filename = os.path.join( "/src", os.path.basename(filename.replace("xyz.py", "ABC.zz")) ) def source_filename(self): return self._source_filename def line_number_range(self, frame): """Map the line number X to X05,X06,X07.""" lineno = frame.f_lineno return lineno*100+5, lineno*100+7 class FileReporter(coverage.FileReporter): """Dead-simple FileReporter.""" def lines(self): return set([105, 106, 107, 205, 206, 207]) def coverage_init(reg, options): # pylint: disable=unused-argument """Called by coverage to initialize the plugins here.""" reg.add_file_tracer(Plugin()) python-coverage-4.5+dfsg.1.orig/tests/test_html.py0000644000076600000620000010164713173515667020224 0ustar staff# -*- coding: utf-8 -*- # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests that HTML generation is awesome.""" import datetime import glob import json import os import os.path import re import sys import coverage import coverage.files import coverage.html from coverage.misc import CoverageException, NotPython, NoSource from tests.coveragetest import CoverageTest from tests.goldtest import CoverageGoldTest from tests.goldtest import change_dir, compare, contains, doesnt_contain, contains_any class HtmlTestHelpers(CoverageTest): """Methods that help with HTML tests.""" def create_initial_files(self): """Create the source files we need to run these tests.""" self.make_file("main_file.py", """\ import helper1, helper2 helper1.func1(12) helper2.func2(12) """) self.make_file("helper1.py", """\ def func1(x): if x % 2: print("odd") """) self.make_file("helper2.py", """\ def func2(x): print("x is %d" % x) """) def run_coverage(self, covargs=None, htmlargs=None): """Run coverage.py on main_file.py, and create an HTML report.""" self.clean_local_file_imports() cov = coverage.Coverage(**(covargs or {})) self.start_import_stop(cov, "main_file") return cov.html_report(**(htmlargs or {})) def remove_html_files(self): """Remove the HTML files created as part of the HTML report.""" os.remove("htmlcov/index.html") os.remove("htmlcov/main_file_py.html") os.remove("htmlcov/helper1_py.html") os.remove("htmlcov/helper2_py.html") def get_html_report_content(self, module): """Return the content of the HTML report for `module`.""" filename = module.replace(".", "_").replace("/", "_") + ".html" filename = os.path.join("htmlcov", filename) with open(filename) as f: return f.read() def get_html_index_content(self): """Return the content of index.html. Timestamps are replaced with a placeholder so that clocks don't matter. """ with open("htmlcov/index.html") as f: index = f.read() index = re.sub( r"created at \d{4}-\d{2}-\d{2} \d{2}:\d{2}", r"created at YYYY-MM-DD HH:MM", index, ) return index def assert_correct_timestamp(self, html): """Extract the timestamp from `html`, and assert it is recent.""" timestamp_pat = r"created at (\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})" m = re.search(timestamp_pat, html) self.assertTrue(m, "Didn't find a timestamp!") timestamp = datetime.datetime(*map(int, m.groups())) # The timestamp only records the minute, so the delta could be from # 12:00 to 12:01:59, or two minutes. self.assert_recent_datetime( timestamp, seconds=120, msg="Timestamp is wrong: {0}".format(timestamp), ) class HtmlDeltaTest(HtmlTestHelpers, CoverageTest): """Tests of the HTML delta speed-ups.""" def setUp(self): super(HtmlDeltaTest, self).setUp() # At least one of our tests monkey-patches the version of coverage.py, # so grab it here to restore it later. self.real_coverage_version = coverage.__version__ self.addCleanup(setattr, coverage, "__version__", self.real_coverage_version) def test_html_created(self): # Test basic HTML generation: files should be created. self.create_initial_files() self.run_coverage() self.assert_exists("htmlcov/index.html") self.assert_exists("htmlcov/main_file_py.html") self.assert_exists("htmlcov/helper1_py.html") self.assert_exists("htmlcov/helper2_py.html") self.assert_exists("htmlcov/style.css") self.assert_exists("htmlcov/coverage_html.js") def test_html_delta_from_source_change(self): # HTML generation can create only the files that have changed. # In this case, helper1 changes because its source is different. self.create_initial_files() self.run_coverage() index1 = self.get_html_index_content() self.remove_html_files() # Now change a file and do it again self.make_file("helper1.py", """\ def func1(x): # A nice function if x % 2: print("odd") """) self.run_coverage() # Only the changed files should have been created. self.assert_exists("htmlcov/index.html") self.assert_exists("htmlcov/helper1_py.html") self.assert_doesnt_exist("htmlcov/main_file_py.html") self.assert_doesnt_exist("htmlcov/helper2_py.html") index2 = self.get_html_index_content() self.assertMultiLineEqual(index1, index2) def test_html_delta_from_coverage_change(self): # HTML generation can create only the files that have changed. # In this case, helper1 changes because its coverage is different. self.create_initial_files() self.run_coverage() self.remove_html_files() # Now change a file and do it again self.make_file("main_file.py", """\ import helper1, helper2 helper1.func1(23) helper2.func2(23) """) self.run_coverage() # Only the changed files should have been created. self.assert_exists("htmlcov/index.html") self.assert_exists("htmlcov/helper1_py.html") self.assert_exists("htmlcov/main_file_py.html") self.assert_doesnt_exist("htmlcov/helper2_py.html") def test_html_delta_from_settings_change(self): # HTML generation can create only the files that have changed. # In this case, everything changes because the coverage.py settings # have changed. self.create_initial_files() self.run_coverage(covargs=dict(omit=[])) index1 = self.get_html_index_content() self.remove_html_files() self.run_coverage(covargs=dict(omit=['xyzzy*'])) # All the files have been reported again. self.assert_exists("htmlcov/index.html") self.assert_exists("htmlcov/helper1_py.html") self.assert_exists("htmlcov/main_file_py.html") self.assert_exists("htmlcov/helper2_py.html") index2 = self.get_html_index_content() self.assertMultiLineEqual(index1, index2) def test_html_delta_from_coverage_version_change(self): # HTML generation can create only the files that have changed. # In this case, everything changes because the coverage.py version has # changed. self.create_initial_files() self.run_coverage() index1 = self.get_html_index_content() self.remove_html_files() # "Upgrade" coverage.py! coverage.__version__ = "XYZZY" self.run_coverage() # All the files have been reported again. self.assert_exists("htmlcov/index.html") self.assert_exists("htmlcov/helper1_py.html") self.assert_exists("htmlcov/main_file_py.html") self.assert_exists("htmlcov/helper2_py.html") index2 = self.get_html_index_content() fixed_index2 = index2.replace("XYZZY", self.real_coverage_version) self.assertMultiLineEqual(index1, fixed_index2) def test_file_becomes_100(self): self.create_initial_files() self.run_coverage() # Now change a file and do it again self.make_file("main_file.py", """\ import helper1, helper2 # helper1 is now 100% helper1.func1(12) helper1.func1(23) """) self.run_coverage(htmlargs=dict(skip_covered=True)) # The 100% file, skipped, shouldn't be here. self.assert_doesnt_exist("htmlcov/helper1_py.html") def test_status_format_change(self): self.create_initial_files() self.run_coverage() self.remove_html_files() with open("htmlcov/status.json") as status_json: status_data = json.load(status_json) self.assertEqual(status_data['format'], 1) status_data['format'] = 2 with open("htmlcov/status.json", "w") as status_json: json.dump(status_data, status_json) self.run_coverage() # All the files have been reported again. self.assert_exists("htmlcov/index.html") self.assert_exists("htmlcov/helper1_py.html") self.assert_exists("htmlcov/main_file_py.html") self.assert_exists("htmlcov/helper2_py.html") class HtmlTitleTest(HtmlTestHelpers, CoverageTest): """Tests of the HTML title support.""" def test_default_title(self): self.create_initial_files() self.run_coverage() index = self.get_html_index_content() self.assertIn("Coverage report", index) self.assertIn("

Coverage report:", index) def test_title_set_in_config_file(self): self.create_initial_files() self.make_file(".coveragerc", "[html]\ntitle = Metrics & stuff!\n") self.run_coverage() index = self.get_html_index_content() self.assertIn("Metrics & stuff!", index) self.assertIn("

Metrics & stuff!:", index) def test_non_ascii_title_set_in_config_file(self): self.create_initial_files() self.make_file(".coveragerc", "[html]\ntitle = «ταБЬℓσ» numbers") self.run_coverage() index = self.get_html_index_content() self.assertIn( "«ταБЬℓσ»" " numbers", index ) self.assertIn( "<h1>«ταБЬℓσ»" " numbers", index ) def test_title_set_in_args(self): self.create_initial_files() self.make_file(".coveragerc", "[html]\ntitle = Good title\n") self.run_coverage(htmlargs=dict(title="«ταБЬℓσ» & stüff!")) index = self.get_html_index_content() self.assertIn( "<title>«ταБЬℓσ»" " & stüff!", index ) self.assertIn( "

«ταБЬℓσ»" " & stüff!:", index ) class HtmlWithUnparsableFilesTest(HtmlTestHelpers, CoverageTest): """Test the behavior when measuring unparsable files.""" def test_dotpy_not_python(self): self.make_file("main.py", "import innocuous") self.make_file("innocuous.py", "a = 1") cov = coverage.Coverage() self.start_import_stop(cov, "main") self.make_file("innocuous.py", "

This isn't python!

") msg = "Couldn't parse '.*innocuous.py' as Python source: .* at line 1" with self.assertRaisesRegex(NotPython, msg): cov.html_report() def test_dotpy_not_python_ignored(self): self.make_file("main.py", "import innocuous") self.make_file("innocuous.py", "a = 2") cov = coverage.Coverage() self.start_import_stop(cov, "main") self.make_file("innocuous.py", "

This isn't python!

") cov.html_report(ignore_errors=True) self.assertEqual( len(cov._warnings), 1, "Expected a warning to be thrown when an invalid python file is parsed") self.assertIn( "Could not parse Python file", cov._warnings[0], "Warning message should be in 'invalid file' warning" ) self.assertIn( "innocuous.py", cov._warnings[0], "Filename should be in 'invalid file' warning" ) self.assert_exists("htmlcov/index.html") # This would be better as a glob, if the HTML layout changes: self.assert_doesnt_exist("htmlcov/innocuous.html") def test_dothtml_not_python(self): # We run a .html file, and when reporting, we can't parse it as # Python. Since it wasn't .py, no error is reported. # Run an "HTML" file self.make_file("innocuous.html", "a = 3") self.run_command("coverage run innocuous.html") # Before reporting, change it to be an HTML file. self.make_file("innocuous.html", "

This isn't python at all!

") output = self.run_command("coverage html") self.assertEqual(output.strip(), "No data to report.") def test_execed_liar_ignored(self): # Jinja2 sets __file__ to be a non-Python file, and then execs code. # If that file contains non-Python code, a TokenError shouldn't # have been raised when writing the HTML report. source = "exec(compile('','','exec'), {'__file__': 'liar.html'})" self.make_file("liar.py", source) self.make_file("liar.html", "{# Whoops, not python code #}") cov = coverage.Coverage() self.start_import_stop(cov, "liar") cov.html_report() self.assert_exists("htmlcov/index.html") def test_execed_liar_ignored_indentation_error(self): # Jinja2 sets __file__ to be a non-Python file, and then execs code. # If that file contains untokenizable code, we shouldn't get an # exception. source = "exec(compile('','','exec'), {'__file__': 'liar.html'})" self.make_file("liar.py", source) # Tokenize will raise an IndentationError if it can't dedent. self.make_file("liar.html", "0\n 2\n 1\n") cov = coverage.Coverage() self.start_import_stop(cov, "liar") cov.html_report() self.assert_exists("htmlcov/index.html") def test_decode_error(self): # https://bitbucket.org/ned/coveragepy/issue/351/files-with-incorrect-encoding-are-ignored # imp.load_module won't load a file with an undecodable character # in a comment, though Python will run them. So we'll change the # file after running. self.make_file("main.py", "import sub.not_ascii") self.make_file("sub/__init__.py") self.make_file("sub/not_ascii.py", """\ # coding: utf-8 a = 1 # Isn't this great?! """) cov = coverage.Coverage() self.start_import_stop(cov, "main") # Create the undecodable version of the file. make_file is too helpful, # so get down and dirty with bytes. with open("sub/not_ascii.py", "wb") as f: f.write(b"# coding: utf-8\na = 1 # Isn't this great?\xcb!\n") with open("sub/not_ascii.py", "rb") as f: undecodable = f.read() self.assertIn(b"?\xcb!", undecodable) cov.html_report() html_report = self.get_html_report_content("sub/not_ascii.py") expected = "# Isn't this great?�!" self.assertIn(expected, html_report) def test_formfeeds(self): # https://bitbucket.org/ned/coveragepy/issue/360/html-reports-get-confused-by-l-in-the-code self.make_file("formfeed.py", "line_one = 1\n\f\nline_two = 2\n") cov = coverage.Coverage() self.start_import_stop(cov, "formfeed") cov.html_report() formfeed_html = self.get_html_report_content("formfeed.py") self.assertIn("line_two", formfeed_html) class HtmlTest(HtmlTestHelpers, CoverageTest): """Moar HTML tests.""" def test_missing_source_file_incorrect_message(self): # https://bitbucket.org/ned/coveragepy/issue/60 self.make_file("thefile.py", "import sub.another\n") self.make_file("sub/__init__.py", "") self.make_file("sub/another.py", "print('another')\n") cov = coverage.Coverage() self.start_import_stop(cov, 'thefile') os.remove("sub/another.py") missing_file = os.path.join(self.temp_dir, "sub", "another.py") missing_file = os.path.realpath(missing_file) msg = "(?i)No source for code: '%s'" % re.escape(missing_file) with self.assertRaisesRegex(NoSource, msg): cov.html_report() def test_extensionless_file_collides_with_extension(self): # It used to be that "program" and "program.py" would both be reported # to "program.html". Now they are not. # https://bitbucket.org/ned/coveragepy/issue/69 self.make_file("program", "import program\n") self.make_file("program.py", "a = 1\n") self.run_command("coverage run program") self.run_command("coverage html") self.assert_exists("htmlcov/index.html") self.assert_exists("htmlcov/program.html") self.assert_exists("htmlcov/program_py.html") def test_has_date_stamp_in_files(self): self.create_initial_files() self.run_coverage() with open("htmlcov/index.html") as f: self.assert_correct_timestamp(f.read()) with open("htmlcov/main_file_py.html") as f: self.assert_correct_timestamp(f.read()) def test_reporting_on_unmeasured_file(self): # It should be ok to ask for an HTML report on a file that wasn't even # measured at all. https://bitbucket.org/ned/coveragepy/issues/403 self.create_initial_files() self.make_file("other.py", "a = 1\n") self.run_coverage(htmlargs=dict(morfs=['other.py'])) self.assert_exists("htmlcov/index.html") self.assert_exists("htmlcov/other_py.html") def test_shining_panda_fix(self): # The ShiningPanda plugin looks for "status.dat" to find HTML reports. # Accommodate them, but only if we are running under Jenkins. self.set_environ("JENKINS_URL", "Something or other") self.create_initial_files() self.run_coverage() self.assert_exists("htmlcov/status.dat") def test_report_skip_covered_no_branches(self): self.make_file("main_file.py", """ import not_covered def normal(): print("z") normal() """) self.make_file("not_covered.py", """ def not_covered(): print("n") """) self.run_coverage(htmlargs=dict(skip_covered=True)) self.assert_exists("htmlcov/index.html") self.assert_doesnt_exist("htmlcov/main_file_py.html") self.assert_exists("htmlcov/not_covered_py.html") def test_report_skip_covered_100(self): self.make_file("main_file.py", """ def normal(): print("z") normal() """) res = self.run_coverage(covargs=dict(source="."), htmlargs=dict(skip_covered=True)) self.assertEqual(res, 100.0) self.assert_doesnt_exist("htmlcov/main_file_py.html") def test_report_skip_covered_branches(self): self.make_file("main_file.py", """ import not_covered def normal(): print("z") normal() """) self.make_file("not_covered.py", """ def not_covered(): print("n") """) self.run_coverage(covargs=dict(branch=True), htmlargs=dict(skip_covered=True)) self.assert_exists("htmlcov/index.html") self.assert_doesnt_exist("htmlcov/main_file_py.html") self.assert_exists("htmlcov/not_covered_py.html") class HtmlStaticFileTest(CoverageTest): """Tests of the static file copying for the HTML report.""" def setUp(self): super(HtmlStaticFileTest, self).setUp() original_path = list(coverage.html.STATIC_PATH) self.addCleanup(setattr, coverage.html, 'STATIC_PATH', original_path) def test_copying_static_files_from_system(self): # Make a new place for static files. self.make_file("static_here/jquery.min.js", "Not Really JQuery!") coverage.html.STATIC_PATH.insert(0, "static_here") self.make_file("main.py", "print(17)") cov = coverage.Coverage() self.start_import_stop(cov, "main") cov.html_report() with open("htmlcov/jquery.min.js") as f: jquery = f.read() self.assertEqual(jquery, "Not Really JQuery!") def test_copying_static_files_from_system_in_dir(self): # Make a new place for static files. INSTALLED = [ "jquery/jquery.min.js", "jquery-hotkeys/jquery.hotkeys.js", "jquery-isonscreen/jquery.isonscreen.js", "jquery-tablesorter/jquery.tablesorter.min.js", ] for fpath in INSTALLED: self.make_file(os.path.join("static_here", fpath), "Not real.") coverage.html.STATIC_PATH.insert(0, "static_here") self.make_file("main.py", "print(17)") cov = coverage.Coverage() self.start_import_stop(cov, "main") cov.html_report() for fpath in INSTALLED: the_file = os.path.basename(fpath) with open(os.path.join("htmlcov", the_file)) as f: contents = f.read() self.assertEqual(contents, "Not real.") def test_cant_find_static_files(self): # Make the path point to useless places. coverage.html.STATIC_PATH = ["/xyzzy"] self.make_file("main.py", "print(17)") cov = coverage.Coverage() self.start_import_stop(cov, "main") msg = "Couldn't find static file u?'.*'" with self.assertRaisesRegex(CoverageException, msg): cov.html_report() class HtmlGoldTests(CoverageGoldTest): """Tests of HTML reporting that use gold files.""" root_dir = 'tests/farm/html' def test_a(self): self.output_dir("out/a") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage() cov.start() import a # pragma: nested cov.stop() # pragma: nested cov.html_report(a, directory='../out/a') compare("gold_a", "out/a", size_within=10, file_pattern="*.html") contains( "out/a/a_py.html", ('if 1 ' '< 2'), (' a ' '= 3'), '67%', ) contains( "out/a/index.html", 'a.py', '67%', '67%', ) def test_b_branch(self): self.output_dir("out/b_branch") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage(branch=True) cov.start() import b # pragma: nested cov.stop() # pragma: nested cov.html_report(b, directory="../out/b_branch") compare("gold_b_branch", "out/b_branch", size_within=10, file_pattern="*.html") contains( "out/b_branch/b_py.html", ('if x ' '< 2'), (' a = ' '3'), '70%', ('8 ↛ 11' 'line 8 didn\'t jump to line 11, ' 'because the condition on line 8 was never false'), ('17 ↛ exit' 'line 17 didn\'t return from function \'two\', ' 'because the condition on line 17 was never false'), ('25 ↛ 26,   ' '25 ↛ 28' '2 missed branches: ' '1) line 25 didn\'t jump to line 26, ' 'because the condition on line 25 was never true, ' '2) line 25 didn\'t jump to line 28, ' 'because the condition on line 25 was never false'), ) contains( "out/b_branch/index.html", 'b.py', '70%', '70%', ) def test_bom(self): self.output_dir("out/bom") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage() cov.start() import bom # pragma: nested cov.stop() # pragma: nested cov.html_report(bom, directory="../out/bom") compare("gold_bom", "out/bom", size_within=10, file_pattern="*.html") contains( "out/bom/bom_py.html", '"3×4 = 12, ÷2 = 6±0"', ) def test_isolatin1(self): self.output_dir("out/isolatin1") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage() cov.start() import isolatin1 # pragma: nested cov.stop() # pragma: nested cov.html_report(isolatin1, directory="../out/isolatin1") compare("gold_isolatin1", "out/isolatin1", size_within=10, file_pattern="*.html") contains( "out/isolatin1/isolatin1_py.html", '"3×4 = 12, ÷2 = 6±0"', ) def test_omit_1(self): self.output_dir("out/omit_1") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage(include=["./*"]) cov.start() import main # pragma: nested # pylint: disable=unused-variable cov.stop() # pragma: nested cov.html_report(directory="../out/omit_1") compare("gold_omit_1", "out/omit_1", size_within=10, file_pattern="*.html") def test_omit_2(self): self.output_dir("out/omit_2") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage(include=["./*"]) cov.start() import main # pragma: nested # pylint: disable=unused-variable cov.stop() # pragma: nested cov.html_report(directory="../out/omit_2", omit=["m1.py"]) compare("gold_omit_2", "out/omit_2", size_within=10, file_pattern="*.html") def test_omit_3(self): self.output_dir("out/omit_3") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage(include=["./*"]) cov.start() import main # pragma: nested # pylint: disable=unused-variable cov.stop() # pragma: nested cov.html_report(directory="../out/omit_3", omit=["m1.py", "m2.py"]) compare("gold_omit_3", "out/omit_3", size_within=10, file_pattern="*.html") def test_omit_4(self): self.output_dir("out/omit_4") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage(config_file="omit4.ini", include=["./*"]) cov.start() import main # pragma: nested # pylint: disable=unused-variable cov.stop() # pragma: nested cov.html_report(directory="../out/omit_4") compare("gold_omit_4", "out/omit_4", size_within=10, file_pattern="*.html") def test_omit_5(self): self.output_dir("out/omit_5") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage(config_file="omit5.ini", include=["./*"]) cov.start() import main # pragma: nested # pylint: disable=unused-variable cov.stop() # pragma: nested cov.html_report() compare("gold_omit_5", "out/omit_5", size_within=10, file_pattern="*.html") def test_other(self): self.output_dir("out/other") with change_dir("src"): # pylint: disable=import-error sys.path.insert(0, "../othersrc") cov = coverage.Coverage(include=["./*", "../othersrc/*"]) cov.start() import here # pragma: nested # pylint: disable=unused-variable cov.stop() # pragma: nested cov.html_report(directory="../out/other") # Different platforms will name the "other" file differently. Rename it for p in glob.glob("out/other/*_other_py.html"): os.rename(p, "out/other/blah_blah_other_py.html") compare("gold_other", "out/other", size_within=10, file_pattern="*.html") contains( "out/other/index.html", 'here.py', 'other_py.html">', 'other.py', ) def test_partial(self): self.output_dir("out/partial") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage(config_file="partial.ini") cov.start() import partial # pragma: nested cov.stop() # pragma: nested cov.html_report(partial, directory="../out/partial") compare("gold_partial", "out/partial", size_within=10, file_pattern="*.html") contains( "out/partial/partial_py.html", '

', '

', '

', # The "if 0" and "if 1" statements are optimized away. '

', # The "raise AssertionError" is excluded by regex in the .ini. '

', ) contains( "out/partial/index.html", 'partial.py', ) contains( "out/partial/index.html", '100%' ) def test_styled(self): self.output_dir("out/styled") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage() cov.start() import a # pragma: nested cov.stop() # pragma: nested cov.html_report(a, directory="../out/styled", extra_css="extra.css") compare("gold_styled", "out/styled", size_within=10, file_pattern="*.html") compare("gold_styled", "out/styled", size_within=10, file_pattern="*.css") contains( "out/styled/a_py.html", '', ('if 1 ' '< 2'), (' a = ' '3'), '67%' ) contains( "out/styled/index.html", '', 'a.py', '67%' ) def test_tabbed(self): self.output_dir("out/tabbed") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage() cov.start() import tabbed # pragma: nested cov.stop() # pragma: nested cov.html_report(tabbed, directory="../out/tabbed") # Editors like to change things, make sure our source file still has tabs. contains("src/tabbed.py", "\tif x:\t\t\t\t\t# look nice") contains( "out/tabbed/tabbed_py.html", '> if ' 'x:' ' ' '# look nice' ) doesnt_contain("out/tabbed/tabbed_py.html", "\t") def test_unicode(self): self.output_dir("out/unicode") with change_dir("src"): # pylint: disable=import-error, redefined-builtin cov = coverage.Coverage() cov.start() import unicode # pragma: nested cov.stop() # pragma: nested cov.html_report(unicode, directory="../out/unicode") compare("gold_unicode", "out/unicode", size_within=10, file_pattern="*.html") contains( "out/unicode/unicode_py.html", '"ʎd˙ǝbɐɹǝʌoɔ"', ) contains_any( "out/unicode/unicode_py.html", '"db40,dd00: x��"', '"db40,dd00: x󠄀"', ) python-coverage-4.5+dfsg.1.orig/tests/conftest.py0000644000076600000620000000076013177624110020024 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """ Pytest auto configuration. This module is run automatically by pytest, to define and enable fixtures. """ import pytest import warnings @pytest.fixture(autouse=True) def set_warnings(): """Enable DeprecationWarnings during all tests.""" warnings.simplefilter("default") warnings.simplefilter("once", DeprecationWarning) python-coverage-4.5+dfsg.1.orig/tests/test_config.py0000644000076600000620000004601713230771753020516 0ustar staff# coding: utf-8 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Test the config file handling for coverage.py""" import mock import coverage from coverage.misc import CoverageException from tests.coveragetest import CoverageTest, UsingModulesMixin class ConfigTest(CoverageTest): """Tests of the different sources of configuration settings.""" def test_default_config(self): # Just constructing a coverage() object gets the right defaults. cov = coverage.Coverage() self.assertFalse(cov.config.timid) self.assertFalse(cov.config.branch) self.assertEqual(cov.config.data_file, ".coverage") def test_arguments(self): # Arguments to the constructor are applied to the configuration. cov = coverage.Coverage(timid=True, data_file="fooey.dat", concurrency="multiprocessing") self.assertTrue(cov.config.timid) self.assertFalse(cov.config.branch) self.assertEqual(cov.config.data_file, "fooey.dat") self.assertEqual(cov.config.concurrency, ["multiprocessing"]) def test_config_file(self): # A .coveragerc file will be read into the configuration. self.make_file(".coveragerc", """\ # This is just a bogus .rc file for testing. [run] timid = True data_file = .hello_kitty.data """) cov = coverage.Coverage() self.assertTrue(cov.config.timid) self.assertFalse(cov.config.branch) self.assertEqual(cov.config.data_file, ".hello_kitty.data") def test_named_config_file(self): # You can name the config file what you like. self.make_file("my_cov.ini", """\ [run] timid = True ; I wouldn't really use this as a data file... data_file = delete.me """) cov = coverage.Coverage(config_file="my_cov.ini") self.assertTrue(cov.config.timid) self.assertFalse(cov.config.branch) self.assertEqual(cov.config.data_file, "delete.me") def test_ignored_config_file(self): # You can disable reading the .coveragerc file. self.make_file(".coveragerc", """\ [run] timid = True data_file = delete.me """) cov = coverage.Coverage(config_file=False) self.assertFalse(cov.config.timid) self.assertFalse(cov.config.branch) self.assertEqual(cov.config.data_file, ".coverage") def test_config_file_then_args(self): # The arguments override the .coveragerc file. self.make_file(".coveragerc", """\ [run] timid = True data_file = weirdo.file """) cov = coverage.Coverage(timid=False, data_file=".mycov") self.assertFalse(cov.config.timid) self.assertFalse(cov.config.branch) self.assertEqual(cov.config.data_file, ".mycov") def test_data_file_from_environment(self): # There's an environment variable for the data_file. self.make_file(".coveragerc", """\ [run] timid = True data_file = weirdo.file """) self.set_environ("COVERAGE_FILE", "fromenv.dat") cov = coverage.Coverage() self.assertEqual(cov.config.data_file, "fromenv.dat") # But the constructor arguments override the environment variable. cov = coverage.Coverage(data_file="fromarg.dat") self.assertEqual(cov.config.data_file, "fromarg.dat") def test_debug_from_environment(self): self.make_file(".coveragerc", """\ [run] debug = dataio, pids """) self.set_environ("COVERAGE_DEBUG", "callers, fooey") cov = coverage.Coverage() self.assertEqual(cov.config.debug, ["dataio", "pids", "callers", "fooey"]) def test_parse_errors(self): # Im-parsable values raise CoverageException, with details. bad_configs_and_msgs = [ ("[run]\ntimid = maybe?\n", r"maybe[?]"), ("timid = 1\n", r"timid = 1"), ("[run\n", r"\[run"), ("[report]\nexclude_lines = foo(\n", r"Invalid \[report\].exclude_lines value 'foo\(': " r"(unbalanced parenthesis|missing \))"), ("[report]\npartial_branches = foo[\n", r"Invalid \[report\].partial_branches value 'foo\[': " r"(unexpected end of regular expression|unterminated character set)"), ("[report]\npartial_branches_always = foo***\n", r"Invalid \[report\].partial_branches_always value " r"'foo\*\*\*': " r"multiple repeat"), ] for bad_config, msg in bad_configs_and_msgs: print("Trying %r" % bad_config) self.make_file(".coveragerc", bad_config) with self.assertRaisesRegex(CoverageException, msg): coverage.Coverage() def test_environment_vars_in_config(self): # Config files can have $envvars in them. self.make_file(".coveragerc", """\ [run] data_file = $DATA_FILE.fooey branch = $OKAY [report] exclude_lines = the_$$one another${THING} x${THING}y x${NOTHING}y huh$${X}what """) self.set_environ("DATA_FILE", "hello-world") self.set_environ("THING", "ZZZ") self.set_environ("OKAY", "yes") cov = coverage.Coverage() self.assertEqual(cov.config.data_file, "hello-world.fooey") self.assertEqual(cov.config.branch, True) self.assertEqual( cov.config.exclude_list, ["the_$one", "anotherZZZ", "xZZZy", "xy", "huh${X}what"] ) def test_tilde_in_config(self): # Config entries that are file paths can be tilde-expanded. self.make_file(".coveragerc", """\ [run] data_file = ~/data.file [html] directory = ~joe/html_dir [xml] output = ~/somewhere/xml.out [report] # Strings that aren't file paths are not tilde-expanded. exclude_lines = ~/data.file ~joe/html_dir """) def expanduser(s): """Fake tilde expansion""" s = s.replace("~/", "/Users/me/") s = s.replace("~joe/", "/Users/joe/") return s with mock.patch.object(coverage.config.os.path, 'expanduser', new=expanduser): cov = coverage.Coverage() self.assertEqual(cov.config.data_file, "/Users/me/data.file") self.assertEqual(cov.config.html_dir, "/Users/joe/html_dir") self.assertEqual(cov.config.xml_output, "/Users/me/somewhere/xml.out") self.assertEqual(cov.config.exclude_list, ["~/data.file", "~joe/html_dir"]) def test_tweaks_after_constructor(self): # set_option can be used after construction to affect the config. cov = coverage.Coverage(timid=True, data_file="fooey.dat") cov.set_option("run:timid", False) self.assertFalse(cov.config.timid) self.assertFalse(cov.config.branch) self.assertEqual(cov.config.data_file, "fooey.dat") self.assertFalse(cov.get_option("run:timid")) self.assertFalse(cov.get_option("run:branch")) self.assertEqual(cov.get_option("run:data_file"), "fooey.dat") def test_tweak_error_checking(self): # Trying to set an unknown config value raises an error. cov = coverage.Coverage() with self.assertRaises(CoverageException): cov.set_option("run:xyzzy", 12) with self.assertRaises(CoverageException): cov.set_option("xyzzy:foo", 12) with self.assertRaises(CoverageException): _ = cov.get_option("run:xyzzy") with self.assertRaises(CoverageException): _ = cov.get_option("xyzzy:foo") def test_tweak_plugin_options(self): # Plugin options have a more flexible syntax. cov = coverage.Coverage() cov.set_option("run:plugins", ["fooey.plugin", "xyzzy.coverage.plugin"]) cov.set_option("fooey.plugin:xyzzy", 17) cov.set_option("xyzzy.coverage.plugin:plugh", ["a", "b"]) with self.assertRaises(CoverageException): cov.set_option("no_such.plugin:foo", 23) self.assertEqual(cov.get_option("fooey.plugin:xyzzy"), 17) self.assertEqual(cov.get_option("xyzzy.coverage.plugin:plugh"), ["a", "b"]) with self.assertRaises(CoverageException): _ = cov.get_option("no_such.plugin:foo") def test_unknown_option(self): self.make_file(".coveragerc", """\ [run] xyzzy = 17 """) msg = r"Unrecognized option '\[run\] xyzzy=' in config file .coveragerc" with self.assertRaisesRegex(CoverageException, msg): _ = coverage.Coverage() def test_misplaced_option(self): self.make_file(".coveragerc", """\ [report] branch = True """) msg = r"Unrecognized option '\[report\] branch=' in config file .coveragerc" with self.assertRaisesRegex(CoverageException, msg): _ = coverage.Coverage() def test_unknown_option_in_other_ini_file(self): self.make_file("setup.cfg", """\ [coverage:run] huh = what? """) msg = (r"Unrecognized option '\[coverage:run\] huh=' in config file setup.cfg") with self.assertRaisesRegex(CoverageException, msg): _ = coverage.Coverage() class ConfigFileTest(UsingModulesMixin, CoverageTest): """Tests of the config file settings in particular.""" # This sample file tries to use lots of variation of syntax... # The {section} placeholder lets us nest these settings in another file. LOTSA_SETTINGS = """\ # This is a settings file for coverage.py [{section}run] timid = yes data_file = something_or_other.dat branch = 1 cover_pylib = TRUE parallel = on concurrency = thread ; this omit is overriden by the omit from [report] omit = twenty source = myapp plugins = plugins.a_plugin plugins.another debug = callers, pids , dataio disable_warnings = abcd , efgh [{section}report] ; these settings affect reporting. exclude_lines = if 0: pragma:?\\s+no cover another_tab ignore_errors = TRUE omit = one, another, some_more, yet_more include = thirty precision = 3 partial_branches = pragma:?\\s+no branch partial_branches_always = if 0: while True: show_missing= TruE skip_covered = TruE [{section}html] directory = c:\\tricky\\dir.somewhere extra_css=something/extra.css title = Title & nums # nums! [{section}xml] output=mycov.xml package_depth = 17 [{section}paths] source = . /home/ned/src/ other = other, /home/ned/other, c:\\Ned\\etc [{section}plugins.a_plugin] hello = world ; comments still work. names = Jane/John/Jenny """ # Just some sample setup.cfg text from the docs. SETUP_CFG = """\ [bdist_rpm] release = 1 packager = Jane Packager doc_files = CHANGES.txt README.txt USAGE.txt doc/ examples/ """ # Just some sample tox.ini text from the docs. TOX_INI = """\ [tox] envlist = py{26,27,33,34,35}-{c,py}tracer skip_missing_interpreters = True [testenv] commands = # Create tests/zipmods.zip, install the egg1 egg python igor.py zip_mods install_egg """ def assert_config_settings_are_correct(self, cov): """Check that `cov` has all the settings from LOTSA_SETTINGS.""" self.assertTrue(cov.config.timid) self.assertEqual(cov.config.data_file, "something_or_other.dat") self.assertTrue(cov.config.branch) self.assertTrue(cov.config.cover_pylib) self.assertEqual(cov.config.debug, ["callers", "pids", "dataio"]) self.assertTrue(cov.config.parallel) self.assertEqual(cov.config.concurrency, ["thread"]) self.assertEqual(cov.config.source, ["myapp"]) self.assertEqual(cov.config.disable_warnings, ["abcd", "efgh"]) self.assertEqual(cov.get_exclude_list(), ["if 0:", r"pragma:?\s+no cover", "another_tab"]) self.assertTrue(cov.config.ignore_errors) self.assertEqual(cov.config.run_omit, ["twenty"]) self.assertEqual(cov.config.report_omit, ["one", "another", "some_more", "yet_more"]) self.assertEqual(cov.config.report_include, ["thirty"]) self.assertEqual(cov.config.precision, 3) self.assertEqual(cov.config.partial_list, [r"pragma:?\s+no branch"]) self.assertEqual(cov.config.partial_always_list, ["if 0:", "while True:"]) self.assertEqual(cov.config.plugins, ["plugins.a_plugin", "plugins.another"]) self.assertTrue(cov.config.show_missing) self.assertTrue(cov.config.skip_covered) self.assertEqual(cov.config.html_dir, r"c:\tricky\dir.somewhere") self.assertEqual(cov.config.extra_css, "something/extra.css") self.assertEqual(cov.config.html_title, "Title & nums # nums!") self.assertEqual(cov.config.xml_output, "mycov.xml") self.assertEqual(cov.config.xml_package_depth, 17) self.assertEqual(cov.config.paths, { 'source': ['.', '/home/ned/src/'], 'other': ['other', '/home/ned/other', 'c:\\Ned\\etc'] }) self.assertEqual(cov.config.get_plugin_options("plugins.a_plugin"), { 'hello': 'world', 'names': 'Jane/John/Jenny', }) self.assertEqual(cov.config.get_plugin_options("plugins.another"), {}) def test_config_file_settings(self): self.make_file(".coveragerc", self.LOTSA_SETTINGS.format(section="")) cov = coverage.Coverage() self.assert_config_settings_are_correct(cov) def check_config_file_settings_in_other_file(self, fname, contents): """Check config will be read from another file, with prefixed sections.""" nested = self.LOTSA_SETTINGS.format(section="coverage:") fname = self.make_file(fname, nested + "\n" + contents) cov = coverage.Coverage() self.assert_config_settings_are_correct(cov) def test_config_file_settings_in_setupcfg(self): self.check_config_file_settings_in_other_file("setup.cfg", self.SETUP_CFG) def test_config_file_settings_in_toxini(self): self.check_config_file_settings_in_other_file("tox.ini", self.TOX_INI) def check_other_config_if_coveragerc_specified(self, fname, contents): """Check that config `fname` is read if .coveragerc is missing, but specified.""" nested = self.LOTSA_SETTINGS.format(section="coverage:") self.make_file(fname, nested + "\n" + contents) cov = coverage.Coverage(config_file=".coveragerc") self.assert_config_settings_are_correct(cov) def test_config_file_settings_in_setupcfg_if_coveragerc_specified(self): self.check_other_config_if_coveragerc_specified("setup.cfg", self.SETUP_CFG) def test_config_file_settings_in_tox_if_coveragerc_specified(self): self.check_other_config_if_coveragerc_specified("tox.ini", self.TOX_INI) def check_other_not_read_if_coveragerc(self, fname): """Check config `fname` is not read if .coveragerc exists.""" self.make_file(".coveragerc", """\ [run] include = foo """) self.make_file(fname, """\ [coverage:run] omit = bar branch = true """) cov = coverage.Coverage() self.assertEqual(cov.config.run_include, ["foo"]) self.assertEqual(cov.config.run_omit, None) self.assertEqual(cov.config.branch, False) def test_setupcfg_only_if_not_coveragerc(self): self.check_other_not_read_if_coveragerc("setup.cfg") def test_toxini_only_if_not_coveragerc(self): self.check_other_not_read_if_coveragerc("tox.ini") def check_other_config_need_prefixes(self, fname): """Check that `fname` sections won't be read if un-prefixed.""" self.make_file(fname, """\ [run] omit = bar branch = true """) cov = coverage.Coverage() self.assertEqual(cov.config.run_omit, None) self.assertEqual(cov.config.branch, False) def test_setupcfg_only_if_prefixed(self): self.check_other_config_need_prefixes("setup.cfg") def test_toxini_only_if_prefixed(self): self.check_other_config_need_prefixes("tox.ini") def test_tox_ini_even_if_setup_cfg(self): # There's a setup.cfg, but no coverage settings in it, so tox.ini # is read. nested = self.LOTSA_SETTINGS.format(section="coverage:") self.make_file("tox.ini", self.TOX_INI + "\n" + nested) self.make_file("setup.cfg", self.SETUP_CFG) cov = coverage.Coverage() self.assert_config_settings_are_correct(cov) def test_read_prefixed_sections_from_explicit_file(self): # You can point to a tox.ini, and it will find [coverage:run] sections nested = self.LOTSA_SETTINGS.format(section="coverage:") self.make_file("tox.ini", self.TOX_INI + "\n" + nested) cov = coverage.Coverage(config_file="tox.ini") self.assert_config_settings_are_correct(cov) def test_non_ascii(self): self.make_file(".coveragerc", """\ [report] exclude_lines = first ✘${TOX_ENVNAME} third [html] title = tabblo & «ταБЬℓσ» # numbers """) self.set_environ("TOX_ENVNAME", "weirdo") cov = coverage.Coverage() self.assertEqual(cov.config.exclude_list, ["first", "✘weirdo", "third"]) self.assertEqual(cov.config.html_title, "tabblo & «ταБЬℓσ» # numbers") def test_unreadable_config(self): # If a config file is explicitly specified, then it is an error for it # to not be readable. bad_files = [ "nosuchfile.txt", ".", ] for bad_file in bad_files: msg = "Couldn't read %r as a config file" % bad_file with self.assertRaisesRegex(CoverageException, msg): coverage.Coverage(config_file=bad_file) def test_nocoveragerc_file_when_specified(self): cov = coverage.Coverage(config_file=".coveragerc") self.assertFalse(cov.config.timid) self.assertFalse(cov.config.branch) self.assertEqual(cov.config.data_file, ".coverage") python-coverage-4.5+dfsg.1.orig/tests/__init__.py0000644000076600000620000000030413146037571017735 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Automated tests. Run with pytest.""" python-coverage-4.5+dfsg.1.orig/tests/test_files.py0000644000076600000620000003114213231510453020331 0ustar staff# coding: utf-8 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for files.py""" import os import os.path import pytest from coverage import files from coverage.files import ( TreeMatcher, FnmatchMatcher, ModuleMatcher, PathAliases, find_python_files, abs_file, actual_path, flat_rootname, ) from coverage.misc import CoverageException from coverage import env from tests.coveragetest import CoverageTest class FilesTest(CoverageTest): """Tests of coverage.files.""" def abs_path(self, p): """Return the absolute path for `p`.""" return os.path.join(os.getcwd(), os.path.normpath(p)) def test_simple(self): self.make_file("hello.py") files.set_relative_directory() self.assertEqual(files.relative_filename(u"hello.py"), u"hello.py") a = self.abs_path("hello.py") self.assertNotEqual(a, "hello.py") self.assertEqual(files.relative_filename(a), "hello.py") def test_peer_directories(self): self.make_file("sub/proj1/file1.py") self.make_file("sub/proj2/file2.py") a1 = self.abs_path("sub/proj1/file1.py") a2 = self.abs_path("sub/proj2/file2.py") d = os.path.normpath("sub/proj1") self.chdir(d) files.set_relative_directory() self.assertEqual(files.relative_filename(a1), "file1.py") self.assertEqual(files.relative_filename(a2), a2) def test_filepath_contains_absolute_prefix_twice(self): # https://bitbucket.org/ned/coveragepy/issue/194 # Build a path that has two pieces matching the absolute path prefix. # Technically, this test doesn't do that on Windows, but drive # letters make that impractical to achieve. files.set_relative_directory() d = abs_file(os.curdir) trick = os.path.splitdrive(d)[1].lstrip(os.path.sep) rel = os.path.join('sub', trick, 'file1.py') self.assertEqual(files.relative_filename(abs_file(rel)), rel) @pytest.mark.parametrize("original, flat", [ (u"a/b/c.py", u"a_b_c_py"), (u"c:\\foo\\bar.html", u"_foo_bar_html"), (u"Montréal/☺/conf.py", u"Montréal_☺_conf_py"), ( # original: u"c:\\lorem\\ipsum\\quia\\dolor\\sit\\amet\\consectetur\\adipisci\\velit\\sed\\quia\\non" u"\\numquam\\eius\\modi\\tempora\\incidunt\\ut\\labore\\et\\dolore\\magnam\\aliquam" u"\\quaerat\\voluptatem\\ut\\enim\\ad\\minima\\veniam\\quis\\nostrum\\exercitationem" u"\\ullam\\corporis\\suscipit\\laboriosam\\Montréal\\☺\\my_program.py", # flat: u"re_et_dolore_magnam_aliquam_quaerat_voluptatem_ut_enim_ad_minima_veniam_quis_" u"nostrum_exercitationem_ullam_corporis_suscipit_laboriosam_Montréal_☺_my_program_py_" u"97eaca41b860faaa1a21349b1f3009bb061cf0a8" ), ]) def test_flat_rootname(original, flat): assert flat_rootname(original) == flat class MatcherTest(CoverageTest): """Tests of file matchers.""" def setUp(self): super(MatcherTest, self).setUp() files.set_relative_directory() def assertMatches(self, matcher, filepath, matches): """The `matcher` should agree with `matches` about `filepath`.""" canonical = files.canonical_filename(filepath) self.assertEqual( matcher.match(canonical), matches, "File %s should have matched as %s" % (filepath, matches) ) def test_tree_matcher(self): matches_to_try = [ (self.make_file("sub/file1.py"), True), (self.make_file("sub/file2.c"), True), (self.make_file("sub2/file3.h"), False), (self.make_file("sub3/file4.py"), True), (self.make_file("sub3/file5.c"), False), ] trees = [ files.canonical_filename("sub"), files.canonical_filename("sub3/file4.py"), ] tm = TreeMatcher(trees) self.assertEqual(tm.info(), trees) for filepath, matches in matches_to_try: self.assertMatches(tm, filepath, matches) def test_module_matcher(self): matches_to_try = [ ('test', True), ('trash', False), ('testing', False), ('test.x', True), ('test.x.y.z', True), ('py', False), ('py.t', False), ('py.test', True), ('py.testing', False), ('py.test.buz', True), ('py.test.buz.baz', True), ('__main__', False), ('mymain', True), ('yourmain', False), ] modules = ['test', 'py.test', 'mymain'] mm = ModuleMatcher(modules) self.assertEqual( mm.info(), modules ) for modulename, matches in matches_to_try: self.assertEqual( mm.match(modulename), matches, modulename, ) def test_fnmatch_matcher(self): matches_to_try = [ (self.make_file("sub/file1.py"), True), (self.make_file("sub/file2.c"), False), (self.make_file("sub2/file3.h"), True), (self.make_file("sub3/file4.py"), True), (self.make_file("sub3/file5.c"), False), ] fnm = FnmatchMatcher(["*.py", "*/sub2/*"]) self.assertEqual(fnm.info(), ["*.py", "*/sub2/*"]) for filepath, matches in matches_to_try: self.assertMatches(fnm, filepath, matches) def test_fnmatch_matcher_overload(self): fnm = FnmatchMatcher(["*x%03d*.txt" % i for i in range(500)]) self.assertMatches(fnm, "x007foo.txt", True) self.assertMatches(fnm, "x123foo.txt", True) self.assertMatches(fnm, "x798bar.txt", False) def test_fnmatch_windows_paths(self): # We should be able to match Windows paths even if we are running on # a non-Windows OS. fnm = FnmatchMatcher(["*/foo.py"]) self.assertMatches(fnm, r"dir\foo.py", True) fnm = FnmatchMatcher([r"*\foo.py"]) self.assertMatches(fnm, r"dir\foo.py", True) class PathAliasesTest(CoverageTest): """Tests for coverage/files.py:PathAliases""" run_in_temp_dir = False def assert_mapped(self, aliases, inp, out): """Assert that `inp` mapped through `aliases` produces `out`. `out` is canonicalized first, since aliases always produce canonicalized paths. """ aliases.pprint() print(inp) print(out) self.assertEqual(aliases.map(inp), files.canonical_filename(out)) def assert_unchanged(self, aliases, inp): """Assert that `inp` mapped through `aliases` is unchanged.""" self.assertEqual(aliases.map(inp), inp) def test_noop(self): aliases = PathAliases() self.assert_unchanged(aliases, '/ned/home/a.py') def test_nomatch(self): aliases = PathAliases() aliases.add('/home/*/src', './mysrc') self.assert_unchanged(aliases, '/home/foo/a.py') def test_wildcard(self): aliases = PathAliases() aliases.add('/ned/home/*/src', './mysrc') self.assert_mapped(aliases, '/ned/home/foo/src/a.py', './mysrc/a.py') aliases = PathAliases() aliases.add('/ned/home/*/src/', './mysrc') self.assert_mapped(aliases, '/ned/home/foo/src/a.py', './mysrc/a.py') def test_no_accidental_match(self): aliases = PathAliases() aliases.add('/home/*/src', './mysrc') self.assert_unchanged(aliases, '/home/foo/srcetc') def test_multiple_patterns(self): aliases = PathAliases() aliases.add('/home/*/src', './mysrc') aliases.add('/lib/*/libsrc', './mylib') self.assert_mapped(aliases, '/home/foo/src/a.py', './mysrc/a.py') self.assert_mapped(aliases, '/lib/foo/libsrc/a.py', './mylib/a.py') def test_cant_have_wildcard_at_end(self): aliases = PathAliases() msg = "Pattern must not end with wildcards." with self.assertRaisesRegex(CoverageException, msg): aliases.add("/ned/home/*", "fooey") with self.assertRaisesRegex(CoverageException, msg): aliases.add("/ned/home/*/", "fooey") with self.assertRaisesRegex(CoverageException, msg): aliases.add("/ned/home/*/*/", "fooey") def test_no_accidental_munging(self): aliases = PathAliases() aliases.add(r'c:\Zoo\boo', 'src/') aliases.add('/home/ned$', 'src/') self.assert_mapped(aliases, r'c:\Zoo\boo\foo.py', 'src/foo.py') self.assert_mapped(aliases, r'/home/ned$/foo.py', 'src/foo.py') def test_paths_are_os_corrected(self): aliases = PathAliases() aliases.add('/home/ned/*/src', './mysrc') aliases.add(r'c:\ned\src', './mysrc') self.assert_mapped(aliases, r'C:\Ned\src\sub\a.py', './mysrc/sub/a.py') aliases = PathAliases() aliases.add('/home/ned/*/src', r'.\mysrc') aliases.add(r'c:\ned\src', r'.\mysrc') self.assert_mapped(aliases, r'/home/ned/foo/src/sub/a.py', r'.\mysrc\sub\a.py') def test_windows_on_linux(self): # https://bitbucket.org/ned/coveragepy/issues/618/problem-when-combining-windows-generated lin = "*/project/module/" win = "*\\project\\module\\" # Try the paths in both orders. for paths in [[lin, win], [win, lin]]: aliases = PathAliases() for path in paths: aliases.add(path, "project/module") self.assert_mapped( aliases, "C:\\a\\path\\somewhere\\coveragepy_test\\project\\module\\tests\\file.py", "project/module/tests/file.py" ) def test_linux_on_windows(self): # https://bitbucket.org/ned/coveragepy/issues/618/problem-when-combining-windows-generated lin = "*/project/module/" win = "*\\project\\module\\" # Try the paths in both orders. for paths in [[lin, win], [win, lin]]: aliases = PathAliases() for path in paths: aliases.add(path, "project\\module") self.assert_mapped( aliases, "C:/a/path/somewhere/coveragepy_test/project/module/tests/file.py", "project\\module\\tests\\file.py" ) def test_multiple_wildcard(self): aliases = PathAliases() aliases.add('/home/jenkins/*/a/*/b/*/django', './django') self.assert_mapped( aliases, '/home/jenkins/xx/a/yy/b/zz/django/foo/bar.py', './django/foo/bar.py' ) def test_leading_wildcard(self): aliases = PathAliases() aliases.add('*/d1', './mysrc1') aliases.add('*/d2', './mysrc2') self.assert_mapped(aliases, '/foo/bar/d1/x.py', './mysrc1/x.py') self.assert_mapped(aliases, '/foo/bar/d2/y.py', './mysrc2/y.py') def test_dot(self): cases = ['.', '..', '../other', '~'] if not env.WINDOWS: # The root test case was added for the manylinux Docker images, # and I'm not sure how it should work on Windows, so skip it. cases += ['/'] for d in cases: aliases = PathAliases() aliases.add(d, '/the/source') the_file = os.path.join(d, 'a.py') the_file = os.path.expanduser(the_file) the_file = os.path.abspath(os.path.realpath(the_file)) assert '~' not in the_file # to be sure the test is pure. self.assert_mapped(aliases, the_file, '/the/source/a.py') class FindPythonFilesTest(CoverageTest): """Tests of `find_python_files`.""" def test_find_python_files(self): self.make_file("sub/a.py") self.make_file("sub/b.py") self.make_file("sub/x.c") # nope: not .py self.make_file("sub/ssub/__init__.py") self.make_file("sub/ssub/s.py") self.make_file("sub/ssub/~s.py") # nope: editor effluvia self.make_file("sub/lab/exp.py") # nope: no __init__.py self.make_file("sub/windows.pyw") py_files = set(find_python_files("sub")) self.assert_same_files(py_files, [ "sub/a.py", "sub/b.py", "sub/ssub/__init__.py", "sub/ssub/s.py", "sub/windows.pyw", ]) class WindowsFileTest(CoverageTest): """Windows-specific tests of file name handling.""" run_in_temp_dir = False def setUp(self): if not env.WINDOWS: self.skipTest("Only need to run Windows tests on Windows.") super(WindowsFileTest, self).setUp() def test_actual_path(self): self.assertEqual(actual_path(r'c:\Windows'), actual_path(r'C:\wINDOWS')) python-coverage-4.5+dfsg.1.orig/tests/test_filereporter.py0000644000076600000620000001015713173515667021755 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for FileReporters""" import os from coverage.plugin import FileReporter from coverage.python import PythonFileReporter from tests.coveragetest import CoverageTest, UsingModulesMixin # pylint: disable=import-error # Unable to import 'aa' (No module named aa) def native(filename): """Make `filename` into a native form.""" return filename.replace("/", os.sep) class FileReporterTest(UsingModulesMixin, CoverageTest): """Tests for FileReporter classes.""" run_in_temp_dir = False def test_filenames(self): acu = PythonFileReporter("aa/afile.py") bcu = PythonFileReporter("aa/bb/bfile.py") ccu = PythonFileReporter("aa/bb/cc/cfile.py") self.assertEqual(acu.relative_filename(), "aa/afile.py") self.assertEqual(bcu.relative_filename(), "aa/bb/bfile.py") self.assertEqual(ccu.relative_filename(), "aa/bb/cc/cfile.py") self.assertEqual(acu.source(), "# afile.py\n") self.assertEqual(bcu.source(), "# bfile.py\n") self.assertEqual(ccu.source(), "# cfile.py\n") def test_odd_filenames(self): acu = PythonFileReporter("aa/afile.odd.py") bcu = PythonFileReporter("aa/bb/bfile.odd.py") b2cu = PythonFileReporter("aa/bb.odd/bfile.py") self.assertEqual(acu.relative_filename(), "aa/afile.odd.py") self.assertEqual(bcu.relative_filename(), "aa/bb/bfile.odd.py") self.assertEqual(b2cu.relative_filename(), "aa/bb.odd/bfile.py") self.assertEqual(acu.source(), "# afile.odd.py\n") self.assertEqual(bcu.source(), "# bfile.odd.py\n") self.assertEqual(b2cu.source(), "# bfile.py\n") def test_modules(self): import aa import aa.bb import aa.bb.cc acu = PythonFileReporter(aa) bcu = PythonFileReporter(aa.bb) ccu = PythonFileReporter(aa.bb.cc) self.assertEqual(acu.relative_filename(), native("aa/__init__.py")) self.assertEqual(bcu.relative_filename(), native("aa/bb/__init__.py")) self.assertEqual(ccu.relative_filename(), native("aa/bb/cc/__init__.py")) self.assertEqual(acu.source(), "# aa\n") self.assertEqual(bcu.source(), "# bb\n") self.assertEqual(ccu.source(), "") # yes, empty def test_module_files(self): import aa.afile import aa.bb.bfile import aa.bb.cc.cfile acu = PythonFileReporter(aa.afile) bcu = PythonFileReporter(aa.bb.bfile) ccu = PythonFileReporter(aa.bb.cc.cfile) self.assertEqual(acu.relative_filename(), native("aa/afile.py")) self.assertEqual(bcu.relative_filename(), native("aa/bb/bfile.py")) self.assertEqual(ccu.relative_filename(), native("aa/bb/cc/cfile.py")) self.assertEqual(acu.source(), "# afile.py\n") self.assertEqual(bcu.source(), "# bfile.py\n") self.assertEqual(ccu.source(), "# cfile.py\n") def test_comparison(self): acu = FileReporter("aa/afile.py") acu2 = FileReporter("aa/afile.py") zcu = FileReporter("aa/zfile.py") bcu = FileReporter("aa/bb/bfile.py") assert acu == acu2 and acu <= acu2 and acu >= acu2 assert acu < zcu and acu <= zcu and acu != zcu assert zcu > acu and zcu >= acu and zcu != acu assert acu < bcu and acu <= bcu and acu != bcu assert bcu > acu and bcu >= acu and bcu != acu def test_egg(self): # Test that we can get files out of eggs, and read their source files. # The egg1 module is installed by an action in igor.py. import egg1 import egg1.egg1 # Verify that we really imported from an egg. If we did, then the # __file__ won't be an actual file, because one of the "directories" # in the path is actually the .egg zip file. self.assert_doesnt_exist(egg1.__file__) ecu = PythonFileReporter(egg1) eecu = PythonFileReporter(egg1.egg1) self.assertEqual(ecu.source(), u"") self.assertIn(u"# My egg file!", eecu.source().splitlines()) python-coverage-4.5+dfsg.1.orig/tests/test_python.py0000644000076600000620000000363313173515667020575 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests of coverage/python.py""" import os import sys import pytest from coverage import env from coverage.python import get_zip_bytes, source_for_file from tests.coveragetest import CoverageTest class GetZipBytesTest(CoverageTest): """Tests of `get_zip_bytes`.""" run_in_temp_dir = False def test_get_encoded_zip_files(self): # See igor.py, do_zipmods, for the text of these files. zip_file = "tests/zipmods.zip" sys.path.append(zip_file) # So we can import the files. for encoding in ["utf8", "gb2312", "hebrew", "shift_jis", "cp1252"]: filename = zip_file + "/encoded_" + encoding + ".py" filename = filename.replace("/", os.sep) zip_data = get_zip_bytes(filename) zip_text = zip_data.decode(encoding) self.assertIn('All OK', zip_text) # Run the code to see that we really got it encoded properly. __import__("encoded_"+encoding) def test_source_for_file(tmpdir): path = tmpdir.join("a.py") src = str(path) assert source_for_file(src) == src assert source_for_file(src + 'c') == src assert source_for_file(src + 'o') == src unknown = src + 'FOO' assert source_for_file(unknown) == unknown @pytest.mark.skipif(not env.WINDOWS, reason="not windows") def test_source_for_file_windows(tmpdir): path = tmpdir.join("a.py") src = str(path) # On windows if a pyw exists, it is an acceptable source path_windows = tmpdir.ensure("a.pyw") assert str(path_windows) == source_for_file(src + 'c') # If both pyw and py exist, py is preferred path.ensure(file=True) assert source_for_file(src + 'c') == src def test_source_for_file_jython(): assert source_for_file("a$py.class") == "a.py" python-coverage-4.5+dfsg.1.orig/tests/test_cmdline.py0000644000076600000620000006771613231146712020665 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Test cmdline.py for coverage.py.""" import os import pprint import re import sys import textwrap import mock import pytest import coverage import coverage.cmdline from coverage import env from coverage.config import CoverageConfig from coverage.data import CoverageData, CoverageDataFiles from coverage.misc import ExceptionDuringRun from tests.coveragetest import CoverageTest, OK, ERR, command_line class BaseCmdLineTest(CoverageTest): """Tests of execution paths through the command line interpreter.""" run_in_temp_dir = False # Make a dict mapping function names to the default values that cmdline.py # uses when calling the function. defaults = mock.Mock() defaults.Coverage( cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None, debug=None, concurrency=None, ) defaults.annotate( directory=None, ignore_errors=None, include=None, omit=None, morfs=[], ) defaults.html_report( directory=None, ignore_errors=None, include=None, omit=None, morfs=[], skip_covered=None, title=None ) defaults.report( ignore_errors=None, include=None, omit=None, morfs=[], show_missing=None, skip_covered=None ) defaults.xml_report( ignore_errors=None, include=None, omit=None, morfs=[], outfile=None, ) DEFAULT_KWARGS = dict((name, kw) for name, _, kw in defaults.mock_calls) def model_object(self): """Return a Mock suitable for use in CoverageScript.""" mk = mock.Mock() # We'll invoke .Coverage as the constructor, and then keep using the # same object as the resulting coverage object. mk.Coverage.return_value = mk # The mock needs to get options, but shouldn't need to set them. config = CoverageConfig() mk.get_option = config.get_option # Get the type right for the result of reporting. mk.report.return_value = 50.0 mk.html_report.return_value = 50.0 mk.xml_report.return_value = 50.0 return mk def mock_command_line(self, args, path_exists=None): """Run `args` through the command line, with a Mock. Returns the Mock it used and the status code returned. """ m = self.model_object() m.path_exists.return_value = path_exists ret = command_line( args, _covpkg=m, _run_python_file=m.run_python_file, _run_python_module=m.run_python_module, _help_fn=m.help_fn, _path_exists=m.path_exists, ) return m, ret def cmd_executes(self, args, code, ret=OK, path_exists=None): """Assert that the `args` end up executing the sequence in `code`.""" m1, r1 = self.mock_command_line(args, path_exists=path_exists) self.assertEqual(r1, ret, "Wrong status: got %r, wanted %r" % (r1, ret)) # Remove all indentation, and change ".foo()" to "m2.foo()". code = re.sub(r"(?m)^\s+", "", code) code = re.sub(r"(?m)^\.", "m2.", code) m2 = self.model_object() m2.path_exists.return_value = path_exists code_obj = compile(code, "", "exec") eval(code_obj, globals(), {'m2': m2}) # pylint: disable=eval-used # Many of our functions take a lot of arguments, and cmdline.py # calls them with many. But most of them are just the defaults, which # we don't want to have to repeat in all tests. For each call, apply # the defaults. This lets the tests just mention the interesting ones. for name, _, kwargs in m2.method_calls: for k, v in self.DEFAULT_KWARGS.get(name, {}).items(): if k not in kwargs: kwargs[k] = v self.assert_same_method_calls(m1, m2) def cmd_executes_same(self, args1, args2): """Assert that the `args1` executes the same as `args2`.""" m1, r1 = self.mock_command_line(args1) m2, r2 = self.mock_command_line(args2) self.assertEqual(r1, r2) self.assert_same_method_calls(m1, m2) def assert_same_method_calls(self, m1, m2): """Assert that `m1.method_calls` and `m2.method_calls` are the same.""" # Use a real equality comparison, but if it fails, use a nicer assert # so we can tell what's going on. We have to use the real == first due # to CmdOptionParser.__eq__ if m1.method_calls != m2.method_calls: pp1 = pprint.pformat(m1.method_calls) pp2 = pprint.pformat(m2.method_calls) self.assertMultiLineEqual(pp1+'\n', pp2+'\n') def cmd_help(self, args, help_msg=None, topic=None, ret=ERR): """Run a command line, and check that it prints the right help. Only the last function call in the mock is checked, which should be the help message that we want to see. """ m, r = self.mock_command_line(args) self.assertEqual(r, ret, "Wrong status: got %s, wanted %s" % (r, ret)) if help_msg: self.assertEqual(m.method_calls[-1], ('help_fn', (help_msg,), {})) else: self.assertEqual(m.method_calls[-1], ('help_fn', (), {'topic': topic})) class BaseCmdLineTestTest(BaseCmdLineTest): """Tests that our BaseCmdLineTest helpers work.""" def test_assert_same_method_calls(self): # All the other tests here use self.cmd_executes_same in successful # ways, so here we just check that it fails. with self.assertRaises(AssertionError): self.cmd_executes_same("run", "debug") class CmdLineTest(BaseCmdLineTest): """Tests of the coverage.py command line.""" def test_annotate(self): # coverage annotate [-d DIR] [-i] [--omit DIR,...] [FILE1 FILE2 ...] self.cmd_executes("annotate", """\ .Coverage() .load() .annotate() """) self.cmd_executes("annotate -d dir1", """\ .Coverage() .load() .annotate(directory="dir1") """) self.cmd_executes("annotate -i", """\ .Coverage() .load() .annotate(ignore_errors=True) """) self.cmd_executes("annotate --omit fooey", """\ .Coverage(omit=["fooey"]) .load() .annotate(omit=["fooey"]) """) self.cmd_executes("annotate --omit fooey,booey", """\ .Coverage(omit=["fooey", "booey"]) .load() .annotate(omit=["fooey", "booey"]) """) self.cmd_executes("annotate mod1", """\ .Coverage() .load() .annotate(morfs=["mod1"]) """) self.cmd_executes("annotate mod1 mod2 mod3", """\ .Coverage() .load() .annotate(morfs=["mod1", "mod2", "mod3"]) """) def test_combine(self): # coverage combine with args self.cmd_executes("combine datadir1", """\ .Coverage() .combine(["datadir1"], strict=True) .save() """) # coverage combine, appending self.cmd_executes("combine --append datadir1", """\ .Coverage() .load() .combine(["datadir1"], strict=True) .save() """) # coverage combine without args self.cmd_executes("combine", """\ .Coverage() .combine(None, strict=True) .save() """) def test_combine_doesnt_confuse_options_with_args(self): # https://bitbucket.org/ned/coveragepy/issues/385/coverage-combine-doesnt-work-with-rcfile self.cmd_executes("combine --rcfile cov.ini", """\ .Coverage(config_file='cov.ini') .combine(None, strict=True) .save() """) self.cmd_executes("combine --rcfile cov.ini data1 data2/more", """\ .Coverage(config_file='cov.ini') .combine(["data1", "data2/more"], strict=True) .save() """) def test_debug(self): self.cmd_help("debug", "What information would you like: config, data, sys?") self.cmd_help("debug foo", "Don't know what you mean by 'foo'") def test_debug_sys(self): self.command_line("debug sys") out = self.stdout() self.assertIn("version:", out) self.assertIn("data_path:", out) def test_debug_config(self): self.command_line("debug config") out = self.stdout() self.assertIn("cover_pylib:", out) self.assertIn("skip_covered:", out) def test_erase(self): # coverage erase self.cmd_executes("erase", """\ .Coverage() .erase() """) def test_version(self): # coverage --version self.cmd_help("--version", topic="version", ret=OK) def test_help_option(self): # coverage -h self.cmd_help("-h", topic="help", ret=OK) self.cmd_help("--help", topic="help", ret=OK) def test_help_command(self): self.cmd_executes("help", ".help_fn(topic='help')") def test_cmd_help(self): self.cmd_executes("run --help", ".help_fn(parser='')") self.cmd_executes_same("help run", "run --help") def test_html(self): # coverage html -d DIR [-i] [--omit DIR,...] [FILE1 FILE2 ...] self.cmd_executes("html", """\ .Coverage() .load() .html_report() """) self.cmd_executes("html -d dir1", """\ .Coverage() .load() .html_report(directory="dir1") """) self.cmd_executes("html -i", """\ .Coverage() .load() .html_report(ignore_errors=True) """) self.cmd_executes("html --omit fooey", """\ .Coverage(omit=["fooey"]) .load() .html_report(omit=["fooey"]) """) self.cmd_executes("html --omit fooey,booey", """\ .Coverage(omit=["fooey", "booey"]) .load() .html_report(omit=["fooey", "booey"]) """) self.cmd_executes("html mod1", """\ .Coverage() .load() .html_report(morfs=["mod1"]) """) self.cmd_executes("html mod1 mod2 mod3", """\ .Coverage() .load() .html_report(morfs=["mod1", "mod2", "mod3"]) """) self.cmd_executes("html --title=Hello_there", """\ .Coverage() .load() .html_report(title='Hello_there') """) def test_report(self): # coverage report [-m] [-i] [-o DIR,...] [FILE1 FILE2 ...] self.cmd_executes("report", """\ .Coverage() .load() .report(show_missing=None) """) self.cmd_executes("report -i", """\ .Coverage() .load() .report(ignore_errors=True) """) self.cmd_executes("report -m", """\ .Coverage() .load() .report(show_missing=True) """) self.cmd_executes("report --omit fooey", """\ .Coverage(omit=["fooey"]) .load() .report(omit=["fooey"]) """) self.cmd_executes("report --omit fooey,booey", """\ .Coverage(omit=["fooey", "booey"]) .load() .report(omit=["fooey", "booey"]) """) self.cmd_executes("report mod1", """\ .Coverage() .load() .report(morfs=["mod1"]) """) self.cmd_executes("report mod1 mod2 mod3", """\ .Coverage() .load() .report(morfs=["mod1", "mod2", "mod3"]) """) self.cmd_executes("report --skip-covered", """\ .Coverage() .load() .report(skip_covered=True) """) def test_run(self): # coverage run [-p] [-L] [--timid] MODULE.py [ARG1 ARG2 ...] # run calls coverage.erase first. self.cmd_executes("run foo.py", """\ .Coverage() .erase() .start() .run_python_file('foo.py', ['foo.py']) .stop() .save() """) # run -a combines with an existing data file before saving. self.cmd_executes("run -a foo.py", """\ .Coverage() .start() .run_python_file('foo.py', ['foo.py']) .stop() .path_exists('.coverage') .combine(data_paths=['.coverage']) .save() """, path_exists=True) # run -a doesn't combine anything if the data file doesn't exist. self.cmd_executes("run -a foo.py", """\ .Coverage() .start() .run_python_file('foo.py', ['foo.py']) .stop() .path_exists('.coverage') .save() """, path_exists=False) # --timid sets a flag, and program arguments get passed through. self.cmd_executes("run --timid foo.py abc 123", """\ .Coverage(timid=True) .erase() .start() .run_python_file('foo.py', ['foo.py', 'abc', '123']) .stop() .save() """) # -L sets a flag, and flags for the program don't confuse us. self.cmd_executes("run -p -L foo.py -a -b", """\ .Coverage(cover_pylib=True, data_suffix=True) .erase() .start() .run_python_file('foo.py', ['foo.py', '-a', '-b']) .stop() .save() """) self.cmd_executes("run --branch foo.py", """\ .Coverage(branch=True) .erase() .start() .run_python_file('foo.py', ['foo.py']) .stop() .save() """) self.cmd_executes("run --rcfile=myrc.rc foo.py", """\ .Coverage(config_file="myrc.rc") .erase() .start() .run_python_file('foo.py', ['foo.py']) .stop() .save() """) self.cmd_executes("run --include=pre1,pre2 foo.py", """\ .Coverage(include=["pre1", "pre2"]) .erase() .start() .run_python_file('foo.py', ['foo.py']) .stop() .save() """) self.cmd_executes("run --omit=opre1,opre2 foo.py", """\ .Coverage(omit=["opre1", "opre2"]) .erase() .start() .run_python_file('foo.py', ['foo.py']) .stop() .save() """) self.cmd_executes("run --include=pre1,pre2 --omit=opre1,opre2 foo.py", """\ .Coverage(include=["pre1", "pre2"], omit=["opre1", "opre2"]) .erase() .start() .run_python_file('foo.py', ['foo.py']) .stop() .save() """) self.cmd_executes("run --source=quux,hi.there,/home/bar foo.py", """\ .Coverage(source=["quux", "hi.there", "/home/bar"]) .erase() .start() .run_python_file('foo.py', ['foo.py']) .stop() .save() """) self.cmd_executes("run --concurrency=gevent foo.py", """\ .Coverage(concurrency='gevent') .erase() .start() .run_python_file('foo.py', ['foo.py']) .stop() .save() """) self.cmd_executes("run --concurrency=multiprocessing foo.py", """\ .Coverage(concurrency='multiprocessing') .erase() .start() .run_python_file('foo.py', ['foo.py']) .stop() .save() """) def test_bad_concurrency(self): self.command_line("run --concurrency=nothing", ret=ERR) err = self.stderr() self.assertIn("option --concurrency: invalid choice: 'nothing'", err) def test_no_multiple_concurrency(self): # You can't use multiple concurrency values on the command line. # I would like to have a better message about not allowing multiple # values for this option, but optparse is not that flexible. self.command_line("run --concurrency=multiprocessing,gevent foo.py", ret=ERR) err = self.stderr() self.assertIn("option --concurrency: invalid choice: 'multiprocessing,gevent'", err) def test_multiprocessing_needs_config_file(self): # You can't use command-line args to add options to multiprocessing # runs, since they won't make it to the subprocesses. You need to use a # config file. self.command_line("run --concurrency=multiprocessing --branch foo.py", ret=ERR) self.assertIn( "Options affecting multiprocessing must be specified in a configuration file.", self.stderr() ) def test_run_debug(self): self.cmd_executes("run --debug=opt1 foo.py", """\ .Coverage(debug=["opt1"]) .erase() .start() .run_python_file('foo.py', ['foo.py']) .stop() .save() """) self.cmd_executes("run --debug=opt1,opt2 foo.py", """\ .Coverage(debug=["opt1","opt2"]) .erase() .start() .run_python_file('foo.py', ['foo.py']) .stop() .save() """) def test_run_module(self): self.cmd_executes("run -m mymodule", """\ .Coverage() .erase() .start() .run_python_module('mymodule', ['mymodule']) .stop() .save() """) self.cmd_executes("run -m mymodule -qq arg1 arg2", """\ .Coverage() .erase() .start() .run_python_module('mymodule', ['mymodule', '-qq', 'arg1', 'arg2']) .stop() .save() """) self.cmd_executes("run --branch -m mymodule", """\ .Coverage(branch=True) .erase() .start() .run_python_module('mymodule', ['mymodule']) .stop() .save() """) self.cmd_executes_same("run -m mymodule", "run --module mymodule") def test_run_nothing(self): self.command_line("run", ret=ERR) self.assertIn("Nothing to do", self.stderr()) def test_cant_append_parallel(self): self.command_line("run --append --parallel-mode foo.py", ret=ERR) self.assertIn("Can't append to data files in parallel mode.", self.stderr()) def test_xml(self): # coverage xml [-i] [--omit DIR,...] [FILE1 FILE2 ...] self.cmd_executes("xml", """\ .Coverage() .load() .xml_report() """) self.cmd_executes("xml -i", """\ .Coverage() .load() .xml_report(ignore_errors=True) """) self.cmd_executes("xml -o myxml.foo", """\ .Coverage() .load() .xml_report(outfile="myxml.foo") """) self.cmd_executes("xml -o -", """\ .Coverage() .load() .xml_report(outfile="-") """) self.cmd_executes("xml --omit fooey", """\ .Coverage(omit=["fooey"]) .load() .xml_report(omit=["fooey"]) """) self.cmd_executes("xml --omit fooey,booey", """\ .Coverage(omit=["fooey", "booey"]) .load() .xml_report(omit=["fooey", "booey"]) """) self.cmd_executes("xml mod1", """\ .Coverage() .load() .xml_report(morfs=["mod1"]) """) self.cmd_executes("xml mod1 mod2 mod3", """\ .Coverage() .load() .xml_report(morfs=["mod1", "mod2", "mod3"]) """) def test_no_arguments_at_all(self): self.cmd_help("", topic="minimum_help", ret=OK) def test_bad_command(self): self.cmd_help("xyzzy", "Unknown command: 'xyzzy'") class CmdLineWithFilesTest(BaseCmdLineTest): """Test the command line in ways that need temp files.""" run_in_temp_dir = True no_files_in_temp_dir = True def test_debug_data(self): data = CoverageData() data.add_lines({ "file1.py": dict.fromkeys(range(1, 18)), "file2.py": dict.fromkeys(range(1, 24)), }) data.add_file_tracers({"file1.py": "a_plugin"}) data_files = CoverageDataFiles() data_files.write(data) self.command_line("debug data") self.assertMultiLineEqual(self.stdout(), textwrap.dedent("""\ -- data ------------------------------------------------------ path: FILENAME has_arcs: False 2 files: file1.py: 17 lines [a_plugin] file2.py: 23 lines """).replace("FILENAME", data_files.filename)) def test_debug_data_with_no_data(self): data_files = CoverageDataFiles() self.command_line("debug data") self.assertMultiLineEqual(self.stdout(), textwrap.dedent("""\ -- data ------------------------------------------------------ path: FILENAME No data collected """).replace("FILENAME", data_files.filename)) class CmdLineStdoutTest(BaseCmdLineTest): """Test the command line with real stdout output.""" def test_minimum_help(self): self.command_line("") out = self.stdout() self.assertIn("Code coverage for Python.", out) self.assertLess(out.count("\n"), 4) def test_version(self): self.command_line("--version") out = self.stdout() self.assertIn("ersion ", out) if env.C_TRACER: self.assertIn("with C extension", out) else: self.assertIn("without C extension", out) self.assertLess(out.count("\n"), 4) def test_help_contains_command_name(self): # Command name should be present in help output. if env.JYTHON: self.skipTest("Jython gets mad if you patch sys.argv") fake_command_path = "lorem/ipsum/dolor".replace("/", os.sep) expected_command_name = "dolor" fake_argv = [fake_command_path, "sit", "amet"] with mock.patch.object(sys, 'argv', new=fake_argv): self.command_line("help") out = self.stdout() self.assertIn(expected_command_name, out) def test_help_contains_command_name_from_package(self): # Command package name should be present in help output. # # When the main module is actually a package's `__main__` module, the resulting command line # has the `__main__.py` file's patch as the command name. Instead, the command name should # be derived from the package name. if env.JYTHON: self.skipTest("Jython gets mad if you patch sys.argv") fake_command_path = "lorem/ipsum/dolor/__main__.py".replace("/", os.sep) expected_command_name = "dolor" fake_argv = [fake_command_path, "sit", "amet"] with mock.patch.object(sys, 'argv', new=fake_argv): self.command_line("help") out = self.stdout() self.assertIn(expected_command_name, out) def test_help(self): self.command_line("help") out = self.stdout() self.assertIn("readthedocs.io", out) self.assertGreater(out.count("\n"), 10) def test_cmd_help(self): self.command_line("help run") out = self.stdout() self.assertIn("", out) self.assertIn("--timid", out) self.assertGreater(out.count("\n"), 10) def test_unknown_topic(self): # Should probably be an ERR return, but meh. self.command_line("help foobar") self.assertEqual(self.stdout(), "Don't know topic 'foobar'\n") def test_error(self): self.command_line("fooey kablooey", ret=ERR) err = self.stderr() self.assertIn("fooey", err) self.assertIn("help", err) class CmdMainTest(CoverageTest): """Tests of coverage.cmdline.main(), using mocking for isolation.""" run_in_temp_dir = False class CoverageScriptStub(object): """A stub for coverage.cmdline.CoverageScript, used by CmdMainTest.""" def command_line(self, argv): """Stub for command_line, the arg determines what it will do.""" if argv[0] == 'hello': print("Hello, world!") elif argv[0] == 'raise': try: raise Exception("oh noes!") except: raise ExceptionDuringRun(*sys.exc_info()) elif argv[0] == 'internalraise': raise ValueError("coverage is broken") elif argv[0] == 'exit': sys.exit(23) else: raise AssertionError("Bad CoverageScriptStub: %r" % (argv,)) return 0 def setUp(self): super(CmdMainTest, self).setUp() old_CoverageScript = coverage.cmdline.CoverageScript coverage.cmdline.CoverageScript = self.CoverageScriptStub self.addCleanup(setattr, coverage.cmdline, 'CoverageScript', old_CoverageScript) def test_normal(self): ret = coverage.cmdline.main(['hello']) self.assertEqual(ret, 0) self.assertEqual(self.stdout(), "Hello, world!\n") def test_raise(self): ret = coverage.cmdline.main(['raise']) self.assertEqual(ret, 1) self.assertEqual(self.stdout(), "") err = self.stderr().split('\n') self.assertEqual(err[0], 'Traceback (most recent call last):') self.assertEqual(err[-3], ' raise Exception("oh noes!")') self.assertEqual(err[-2], 'Exception: oh noes!') def test_internalraise(self): with self.assertRaisesRegex(ValueError, "coverage is broken"): coverage.cmdline.main(['internalraise']) def test_exit(self): ret = coverage.cmdline.main(['exit']) self.assertEqual(ret, 23) class CoverageReportingFake(object): """A fake Coverage and Coverage.coverage test double.""" # pylint: disable=missing-docstring def __init__(self, report_result, html_result, xml_result): self.config = CoverageConfig() self.report_result = report_result self.html_result = html_result self.xml_result = xml_result def Coverage(self, *args_unused, **kwargs_unused): return self def set_option(self, optname, optvalue): self.config.set_option(optname, optvalue) def get_option(self, optname): return self.config.get_option(optname) def load(self): pass def report(self, *args_unused, **kwargs_unused): return self.report_result def html_report(self, *args_unused, **kwargs_unused): return self.html_result def xml_report(self, *args_unused, **kwargs_unused): return self.xml_result @pytest.mark.parametrize("results, fail_under, cmd, ret", [ # Command-line switch properly checks the result of reporting functions. ((20, 30, 40), None, "report --fail-under=19", 0), ((20, 30, 40), None, "report --fail-under=21", 2), ((20, 30, 40), None, "html --fail-under=29", 0), ((20, 30, 40), None, "html --fail-under=31", 2), ((20, 30, 40), None, "xml --fail-under=39", 0), ((20, 30, 40), None, "xml --fail-under=41", 2), # Configuration file setting properly checks the result of reporting. ((20, 30, 40), 19, "report", 0), ((20, 30, 40), 21, "report", 2), ((20, 30, 40), 29, "html", 0), ((20, 30, 40), 31, "html", 2), ((20, 30, 40), 39, "xml", 0), ((20, 30, 40), 41, "xml", 2), # Command-line overrides configuration. ((20, 30, 40), 19, "report --fail-under=21", 2), ]) def test_fail_under(results, fail_under, cmd, ret): cov = CoverageReportingFake(*results) if fail_under is not None: cov.set_option("report:fail_under", fail_under) ret_actual = command_line(cmd, _covpkg=cov) assert ret_actual == ret python-coverage-4.5+dfsg.1.orig/tests/eggsrc/0000755000076600000620000000000013235414515017075 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/eggsrc/setup.py0000644000076600000620000000044213146037571020613 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt from setuptools import setup setup( name="covtestegg1", packages=['egg1'], zip_safe=True, install_requires=[], ) python-coverage-4.5+dfsg.1.orig/tests/eggsrc/egg1/0000755000076600000620000000000013235414515017720 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/eggsrc/egg1/__init__.py0000644000076600000620000000000013146037571022023 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/eggsrc/egg1/egg1.py0000644000076600000620000000032613146037571021122 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # My egg file! walrus = "Eggman" says = "coo-coo cachoo" python-coverage-4.5+dfsg.1.orig/tests/test_execfile.py0000644000076600000620000001772313225544333021033 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for coverage.execfile""" import compileall import json import os import os.path import re from coverage import env from coverage.backward import binary_bytes from coverage.execfile import run_python_file, run_python_module from coverage.misc import NoCode, NoSource from tests.coveragetest import CoverageTest, TESTS_DIR, UsingModulesMixin TRY_EXECFILE = os.path.join(TESTS_DIR, "modules/process_test/try_execfile.py") class RunFileTest(CoverageTest): """Test cases for `run_python_file`.""" def test_run_python_file(self): run_python_file(TRY_EXECFILE, [TRY_EXECFILE, "arg1", "arg2"]) mod_globs = json.loads(self.stdout()) # The file should think it is __main__ self.assertEqual(mod_globs['__name__'], "__main__") # It should seem to come from a file named try_execfile.py dunder_file = os.path.basename(mod_globs['__file__']) self.assertEqual(dunder_file, "try_execfile.py") # It should have its correct module data. self.assertEqual(mod_globs['__doc__'].splitlines()[0], "Test file for run_python_file.") self.assertEqual(mod_globs['DATA'], "xyzzy") self.assertEqual(mod_globs['FN_VAL'], "my_fn('fooey')") # It must be self-importable as __main__. self.assertEqual(mod_globs['__main__.DATA'], "xyzzy") # Argv should have the proper values. self.assertEqual(mod_globs['argv0'], TRY_EXECFILE) self.assertEqual(mod_globs['argv1-n'], ["arg1", "arg2"]) # __builtins__ should have the right values, like open(). self.assertEqual(mod_globs['__builtins__.has_open'], True) def test_no_extra_file(self): # Make sure that running a file doesn't create an extra compiled file. self.make_file("xxx", """\ desc = "a non-.py file!" """) self.assertEqual(os.listdir("."), ["xxx"]) run_python_file("xxx", ["xxx"]) self.assertEqual(os.listdir("."), ["xxx"]) def test_universal_newlines(self): # Make sure we can read any sort of line ending. pylines = """# try newlines|print('Hello, world!')|""".split('|') for nl in ('\n', '\r\n', '\r'): with open('nl.py', 'wb') as fpy: fpy.write(nl.join(pylines).encode('utf-8')) run_python_file('nl.py', ['nl.py']) self.assertEqual(self.stdout(), "Hello, world!\n"*3) def test_missing_final_newline(self): # Make sure we can deal with a Python file with no final newline. self.make_file("abrupt.py", """\ if 1: a = 1 print("a is %r" % a) #""") with open("abrupt.py") as f: abrupt = f.read() self.assertEqual(abrupt[-1], '#') run_python_file("abrupt.py", ["abrupt.py"]) self.assertEqual(self.stdout(), "a is 1\n") def test_no_such_file(self): with self.assertRaises(NoSource): run_python_file("xyzzy.py", []) def test_directory_with_main(self): self.make_file("with_main/__main__.py", """\ print("I am __main__") """) run_python_file("with_main", ["with_main"]) self.assertEqual(self.stdout(), "I am __main__\n") def test_directory_without_main(self): self.make_file("without_main/__init__.py", "") with self.assertRaisesRegex(NoSource, "Can't find '__main__' module in 'without_main'"): run_python_file("without_main", ["without_main"]) class RunPycFileTest(CoverageTest): """Test cases for `run_python_file`.""" def make_pyc(self): # pylint: disable=inconsistent-return-statements """Create a .pyc file, and return the relative path to it.""" if env.JYTHON: self.skipTest("Can't make .pyc files on Jython") self.make_file("compiled.py", """\ def doit(): print("I am here!") doit() """) compileall.compile_dir(".", quiet=True) os.remove("compiled.py") # Find the .pyc file! for there, _, files in os.walk("."): # pragma: part covered for f in files: if f.endswith(".pyc"): # pragma: part covered return os.path.join(there, f) def test_running_pyc(self): pycfile = self.make_pyc() run_python_file(pycfile, [pycfile]) self.assertEqual(self.stdout(), "I am here!\n") def test_running_pyo(self): pycfile = self.make_pyc() pyofile = re.sub(r"[.]pyc$", ".pyo", pycfile) self.assertNotEqual(pycfile, pyofile) os.rename(pycfile, pyofile) run_python_file(pyofile, [pyofile]) self.assertEqual(self.stdout(), "I am here!\n") def test_running_pyc_from_wrong_python(self): pycfile = self.make_pyc() # Jam Python 2.1 magic number into the .pyc file. with open(pycfile, "r+b") as fpyc: fpyc.seek(0) fpyc.write(binary_bytes([0x2a, 0xeb, 0x0d, 0x0a])) with self.assertRaisesRegex(NoCode, "Bad magic number in .pyc file"): run_python_file(pycfile, [pycfile]) def test_no_such_pyc_file(self): with self.assertRaisesRegex(NoCode, "No file to run: 'xyzzy.pyc'"): run_python_file("xyzzy.pyc", []) def test_running_py_from_binary(self): # Use make_file to get the bookkeeping. Ideally, it would # be able to write binary files. bf = self.make_file("binary") with open(bf, "wb") as f: f.write(b'\x7fELF\x02\x01\x01\x00\x00\x00') msg = ( r"Couldn't run 'binary' as Python code: " r"(TypeError|ValueError): " r"(" r"compile\(\) expected string without null bytes" # for py2 r"|" r"source code string cannot contain null bytes" # for py3 r")" ) with self.assertRaisesRegex(Exception, msg): run_python_file(bf, [bf]) class RunModuleTest(UsingModulesMixin, CoverageTest): """Test run_python_module.""" run_in_temp_dir = False def test_runmod1(self): run_python_module("runmod1", ["runmod1", "hello"]) self.assertEqual(self.stderr(), "") self.assertEqual(self.stdout(), "runmod1: passed hello\n") def test_runmod2(self): run_python_module("pkg1.runmod2", ["runmod2", "hello"]) self.assertEqual(self.stderr(), "") self.assertEqual(self.stdout(), "pkg1.__init__: pkg1\nrunmod2: passed hello\n") def test_runmod3(self): run_python_module("pkg1.sub.runmod3", ["runmod3", "hello"]) self.assertEqual(self.stderr(), "") self.assertEqual(self.stdout(), "pkg1.__init__: pkg1\nrunmod3: passed hello\n") def test_pkg1_main(self): run_python_module("pkg1", ["pkg1", "hello"]) self.assertEqual(self.stderr(), "") self.assertEqual(self.stdout(), "pkg1.__init__: pkg1\npkg1.__main__: passed hello\n") def test_pkg1_sub_main(self): run_python_module("pkg1.sub", ["pkg1.sub", "hello"]) self.assertEqual(self.stderr(), "") self.assertEqual(self.stdout(), "pkg1.__init__: pkg1\npkg1.sub.__main__: passed hello\n") def test_pkg1_init(self): run_python_module("pkg1.__init__", ["pkg1.__init__", "wut?"]) self.assertEqual(self.stderr(), "") self.assertEqual(self.stdout(), "pkg1.__init__: pkg1\npkg1.__init__: __main__\n") def test_no_such_module(self): with self.assertRaises(NoSource): run_python_module("i_dont_exist", []) with self.assertRaises(NoSource): run_python_module("i.dont_exist", []) with self.assertRaises(NoSource): run_python_module("i.dont.exist", []) def test_no_main(self): with self.assertRaises(NoSource): run_python_module("pkg2", ["pkg2", "hi"]) python-coverage-4.5+dfsg.1.orig/tests/test_backward.py0000644000076600000620000000144613146037571021023 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests that our version shims in backward.py are working.""" from coverage.backunittest import TestCase from coverage.backward import iitems, binary_bytes, bytes_to_ints class BackwardTest(TestCase): """Tests of things from backward.py.""" def test_iitems(self): d = {'a': 1, 'b': 2, 'c': 3} items = [('a', 1), ('b', 2), ('c', 3)] self.assertCountEqual(list(iitems(d)), items) def test_binary_bytes(self): byte_values = [0, 255, 17, 23, 42, 57] bb = binary_bytes(byte_values) self.assertEqual(len(bb), len(byte_values)) self.assertEqual(byte_values, list(bytes_to_ints(bb))) python-coverage-4.5+dfsg.1.orig/tests/plugin_config.py0000644000076600000620000000144513220721537021023 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """A configuring plugin for test_plugins.py to import.""" import coverage class Plugin(coverage.CoveragePlugin): """A configuring plugin for testing.""" def configure(self, config): """Configure all the things!""" opt_name = "report:exclude_lines" exclude_lines = config.get_option(opt_name) exclude_lines.append(r"pragma: custom") exclude_lines.append(r"pragma: or whatever") config.set_option(opt_name, exclude_lines) def coverage_init(reg, options): # pylint: disable=unused-argument """Called by coverage to initialize the plugins here.""" reg.add_configurer(Plugin()) python-coverage-4.5+dfsg.1.orig/tests/test_api.py0000644000076600000620000006730713230767041020022 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for coverage.py's API.""" import fnmatch import os import sys import textwrap import warnings import coverage from coverage import env from coverage.backward import StringIO, import_local_file from coverage.misc import CoverageException from coverage.report import Reporter from tests.coveragetest import CoverageTest, CoverageTestMethodsMixin, TESTS_DIR, UsingModulesMixin class ApiTest(CoverageTest): """Api-oriented tests for coverage.py.""" def clean_files(self, files, pats): """Remove names matching `pats` from `files`, a list of file names.""" good = [] for f in files: for pat in pats: if fnmatch.fnmatch(f, pat): break else: good.append(f) return good def assertFiles(self, files): """Assert that the files here are `files`, ignoring the usual junk.""" here = os.listdir(".") here = self.clean_files(here, ["*.pyc", "__pycache__", "*$py.class"]) self.assertCountEqual(here, files) def test_unexecuted_file(self): cov = coverage.Coverage() self.make_file("mycode.py", """\ a = 1 b = 2 if b == 3: c = 4 d = 5 """) self.make_file("not_run.py", """\ fooey = 17 """) # Import the Python file, executing it. self.start_import_stop(cov, "mycode") _, statements, missing, _ = cov.analysis("not_run.py") self.assertEqual(statements, [1]) self.assertEqual(missing, [1]) def test_filenames(self): self.make_file("mymain.py", """\ import mymod a = 1 """) self.make_file("mymod.py", """\ fooey = 17 """) # Import the Python file, executing it. cov = coverage.Coverage() self.start_import_stop(cov, "mymain") filename, _, _, _ = cov.analysis("mymain.py") self.assertEqual(os.path.basename(filename), "mymain.py") filename, _, _, _ = cov.analysis("mymod.py") self.assertEqual(os.path.basename(filename), "mymod.py") filename, _, _, _ = cov.analysis(sys.modules["mymain"]) self.assertEqual(os.path.basename(filename), "mymain.py") filename, _, _, _ = cov.analysis(sys.modules["mymod"]) self.assertEqual(os.path.basename(filename), "mymod.py") # Import the Python file, executing it again, once it's been compiled # already. cov = coverage.Coverage() self.start_import_stop(cov, "mymain") filename, _, _, _ = cov.analysis("mymain.py") self.assertEqual(os.path.basename(filename), "mymain.py") filename, _, _, _ = cov.analysis("mymod.py") self.assertEqual(os.path.basename(filename), "mymod.py") filename, _, _, _ = cov.analysis(sys.modules["mymain"]) self.assertEqual(os.path.basename(filename), "mymain.py") filename, _, _, _ = cov.analysis(sys.modules["mymod"]) self.assertEqual(os.path.basename(filename), "mymod.py") def test_ignore_stdlib(self): self.make_file("mymain.py", """\ import colorsys a = 1 hls = colorsys.rgb_to_hls(1.0, 0.5, 0.0) """) # Measure without the stdlib. cov1 = coverage.Coverage() self.assertEqual(cov1.config.cover_pylib, False) self.start_import_stop(cov1, "mymain") # some statements were marked executed in mymain.py _, statements, missing, _ = cov1.analysis("mymain.py") self.assertNotEqual(statements, missing) # but none were in colorsys.py _, statements, missing, _ = cov1.analysis("colorsys.py") self.assertEqual(statements, missing) # Measure with the stdlib. cov2 = coverage.Coverage(cover_pylib=True) self.start_import_stop(cov2, "mymain") # some statements were marked executed in mymain.py _, statements, missing, _ = cov2.analysis("mymain.py") self.assertNotEqual(statements, missing) # and some were marked executed in colorsys.py _, statements, missing, _ = cov2.analysis("colorsys.py") self.assertNotEqual(statements, missing) def test_include_can_measure_stdlib(self): self.make_file("mymain.py", """\ import colorsys, random a = 1 r, g, b = [random.random() for _ in range(3)] hls = colorsys.rgb_to_hls(r, g, b) """) # Measure without the stdlib, but include colorsys. cov1 = coverage.Coverage(cover_pylib=False, include=["*/colorsys.py"]) self.start_import_stop(cov1, "mymain") # some statements were marked executed in colorsys.py _, statements, missing, _ = cov1.analysis("colorsys.py") self.assertNotEqual(statements, missing) # but none were in random.py _, statements, missing, _ = cov1.analysis("random.py") self.assertEqual(statements, missing) def test_exclude_list(self): cov = coverage.Coverage() cov.clear_exclude() self.assertEqual(cov.get_exclude_list(), []) cov.exclude("foo") self.assertEqual(cov.get_exclude_list(), ["foo"]) cov.exclude("bar") self.assertEqual(cov.get_exclude_list(), ["foo", "bar"]) self.assertEqual(cov._exclude_regex('exclude'), "(?:foo)|(?:bar)") cov.clear_exclude() self.assertEqual(cov.get_exclude_list(), []) def test_exclude_partial_list(self): cov = coverage.Coverage() cov.clear_exclude(which='partial') self.assertEqual(cov.get_exclude_list(which='partial'), []) cov.exclude("foo", which='partial') self.assertEqual(cov.get_exclude_list(which='partial'), ["foo"]) cov.exclude("bar", which='partial') self.assertEqual(cov.get_exclude_list(which='partial'), ["foo", "bar"]) self.assertEqual( cov._exclude_regex(which='partial'), "(?:foo)|(?:bar)" ) cov.clear_exclude(which='partial') self.assertEqual(cov.get_exclude_list(which='partial'), []) def test_exclude_and_partial_are_separate_lists(self): cov = coverage.Coverage() cov.clear_exclude(which='partial') cov.clear_exclude(which='exclude') cov.exclude("foo", which='partial') self.assertEqual(cov.get_exclude_list(which='partial'), ['foo']) self.assertEqual(cov.get_exclude_list(which='exclude'), []) cov.exclude("bar", which='exclude') self.assertEqual(cov.get_exclude_list(which='partial'), ['foo']) self.assertEqual(cov.get_exclude_list(which='exclude'), ['bar']) cov.exclude("p2", which='partial') cov.exclude("e2", which='exclude') self.assertEqual(cov.get_exclude_list(which='partial'), ['foo', 'p2']) self.assertEqual(cov.get_exclude_list(which='exclude'), ['bar', 'e2']) cov.clear_exclude(which='partial') self.assertEqual(cov.get_exclude_list(which='partial'), []) self.assertEqual(cov.get_exclude_list(which='exclude'), ['bar', 'e2']) cov.clear_exclude(which='exclude') self.assertEqual(cov.get_exclude_list(which='partial'), []) self.assertEqual(cov.get_exclude_list(which='exclude'), []) def test_datafile_default(self): # Default data file behavior: it's .coverage self.make_file("datatest1.py", """\ fooey = 17 """) self.assertFiles(["datatest1.py"]) cov = coverage.Coverage() self.start_import_stop(cov, "datatest1") cov.save() self.assertFiles(["datatest1.py", ".coverage"]) def test_datafile_specified(self): # You can specify the data file name. self.make_file("datatest2.py", """\ fooey = 17 """) self.assertFiles(["datatest2.py"]) cov = coverage.Coverage(data_file="cov.data") self.start_import_stop(cov, "datatest2") cov.save() self.assertFiles(["datatest2.py", "cov.data"]) def test_datafile_and_suffix_specified(self): # You can specify the data file name and suffix. self.make_file("datatest3.py", """\ fooey = 17 """) self.assertFiles(["datatest3.py"]) cov = coverage.Coverage(data_file="cov.data", data_suffix="14") self.start_import_stop(cov, "datatest3") cov.save() self.assertFiles(["datatest3.py", "cov.data.14"]) def test_datafile_from_rcfile(self): # You can specify the data file name in the .coveragerc file self.make_file("datatest4.py", """\ fooey = 17 """) self.make_file(".coveragerc", """\ [run] data_file = mydata.dat """) self.assertFiles(["datatest4.py", ".coveragerc"]) cov = coverage.Coverage() self.start_import_stop(cov, "datatest4") cov.save() self.assertFiles(["datatest4.py", ".coveragerc", "mydata.dat"]) def test_empty_reporting(self): # empty summary reports raise exception, just like the xml report cov = coverage.Coverage() cov.erase() self.assertRaises(CoverageException, cov.report) def make_code1_code2(self): """Create the code1.py and code2.py files.""" self.make_file("code1.py", """\ code1 = 1 """) self.make_file("code2.py", """\ code2 = 1 code2 = 2 """) def check_code1_code2(self, cov): """Check the analysis is correct for code1.py and code2.py.""" _, statements, missing, _ = cov.analysis("code1.py") self.assertEqual(statements, [1]) self.assertEqual(missing, []) _, statements, missing, _ = cov.analysis("code2.py") self.assertEqual(statements, [1, 2]) self.assertEqual(missing, []) def test_start_stop_start_stop(self): self.make_code1_code2() cov = coverage.Coverage() self.start_import_stop(cov, "code1") cov.save() self.start_import_stop(cov, "code2") self.check_code1_code2(cov) def test_start_save_stop(self): self.make_code1_code2() cov = coverage.Coverage() cov.start() import_local_file("code1") # pragma: nested cov.save() # pragma: nested import_local_file("code2") # pragma: nested cov.stop() # pragma: nested self.check_code1_code2(cov) def test_start_save_nostop(self): self.make_code1_code2() cov = coverage.Coverage() cov.start() import_local_file("code1") # pragma: nested cov.save() # pragma: nested import_local_file("code2") # pragma: nested self.check_code1_code2(cov) # pragma: nested # Then stop it, or the test suite gets out of whack. cov.stop() # pragma: nested def test_two_getdata_only_warn_once(self): self.make_code1_code2() cov = coverage.Coverage(source=["."], omit=["code1.py"]) cov.start() import_local_file("code1") # pragma: nested cov.stop() # pragma: nested # We didn't collect any data, so we should get a warning. with self.assert_warnings(cov, ["No data was collected"]): cov.get_data() # But calling get_data a second time with no intervening activity # won't make another warning. with self.assert_warnings(cov, []): cov.get_data() def test_two_getdata_only_warn_once_nostop(self): self.make_code1_code2() cov = coverage.Coverage(source=["."], omit=["code1.py"]) cov.start() import_local_file("code1") # pragma: nested # We didn't collect any data, so we should get a warning. with self.assert_warnings(cov, ["No data was collected"]): # pragma: nested cov.get_data() # pragma: nested # But calling get_data a second time with no intervening activity # won't make another warning. with self.assert_warnings(cov, []): # pragma: nested cov.get_data() # pragma: nested # Then stop it, or the test suite gets out of whack. cov.stop() # pragma: nested def test_two_getdata_warn_twice(self): self.make_code1_code2() cov = coverage.Coverage(source=["."], omit=["code1.py", "code2.py"]) cov.start() import_local_file("code1") # pragma: nested # We didn't collect any data, so we should get a warning. with self.assert_warnings(cov, ["No data was collected"]): # pragma: nested cov.save() # pragma: nested import_local_file("code2") # pragma: nested # Calling get_data a second time after tracing some more will warn again. with self.assert_warnings(cov, ["No data was collected"]): # pragma: nested cov.get_data() # pragma: nested # Then stop it, or the test suite gets out of whack. cov.stop() # pragma: nested def make_good_data_files(self): """Make some good data files.""" self.make_code1_code2() cov = coverage.Coverage(data_suffix=True) self.start_import_stop(cov, "code1") cov.save() cov = coverage.Coverage(data_suffix=True) self.start_import_stop(cov, "code2") cov.save() def make_bad_data_file(self): """Make one bad data file.""" self.make_file(".coverage.foo", """La la la, this isn't coverage data!""") def test_combining_corrupt_data(self): # If you combine a corrupt data file, then you will get a warning, # and the file will remain. self.make_good_data_files() self.make_bad_data_file() cov = coverage.Coverage() warning_regex = ( r"Couldn't read data from '.*\.coverage\.foo': " r"CoverageException: Doesn't seem to be a coverage\.py data file" ) with self.assert_warnings(cov, [warning_regex]): cov.combine() # We got the results from code1 and code2 properly. self.check_code1_code2(cov) # The bad file still exists. self.assert_exists(".coverage.foo") def test_combining_twice(self): self.make_good_data_files() cov1 = coverage.Coverage() cov1.combine() cov1.save() self.check_code1_code2(cov1) cov2 = coverage.Coverage() with self.assertRaisesRegex(CoverageException, r"No data to combine"): cov2.combine(strict=True) cov3 = coverage.Coverage() cov3.combine() # Now the data is empty! _, statements, missing, _ = cov3.analysis("code1.py") self.assertEqual(statements, [1]) self.assertEqual(missing, [1]) _, statements, missing, _ = cov3.analysis("code2.py") self.assertEqual(statements, [1, 2]) self.assertEqual(missing, [1, 2]) def test_warnings(self): self.make_file("hello.py", """\ import sys, os print("Hello") """) cov = coverage.Coverage(source=["sys", "xyzzy", "quux"]) self.start_import_stop(cov, "hello") cov.get_data() out = self.stdout() self.assertIn("Hello\n", out) err = self.stderr() self.assertIn(textwrap.dedent("""\ Coverage.py warning: Module sys has no Python source. (module-not-python) Coverage.py warning: Module xyzzy was never imported. (module-not-imported) Coverage.py warning: Module quux was never imported. (module-not-imported) Coverage.py warning: No data was collected. (no-data-collected) """), err) def test_warnings_suppressed(self): self.make_file("hello.py", """\ import sys, os print("Hello") """) self.make_file(".coveragerc", """\ [run] disable_warnings = no-data-collected, module-not-imported """) cov = coverage.Coverage(source=["sys", "xyzzy", "quux"]) self.start_import_stop(cov, "hello") cov.get_data() out = self.stdout() self.assertIn("Hello\n", out) err = self.stderr() self.assertIn(textwrap.dedent("""\ Coverage.py warning: Module sys has no Python source. (module-not-python) """), err) self.assertNotIn("module-not-imported", err) self.assertNotIn("no-data-collected", err) def test_source_and_include_dont_conflict(self): # A bad fix made this case fail: https://bitbucket.org/ned/coveragepy/issues/541 self.make_file("a.py", "import b\na = 1") self.make_file("b.py", "b = 1") self.make_file(".coveragerc", """\ [run] source = . """) # Just like: coverage run a.py cov = coverage.Coverage() self.start_import_stop(cov, "a") cov.save() # Run the equivalent of: coverage report --include=b.py cov = coverage.Coverage(include=["b.py"]) cov.load() # There should be no exception. At one point, report() threw: # CoverageException: --include and --source are mutually exclusive cov.report() self.assertEqual(self.stdout(), textwrap.dedent("""\ Name Stmts Miss Cover --------------------------- b.py 1 0 100% """)) class NamespaceModuleTest(UsingModulesMixin, CoverageTest): """Test PEP-420 namespace modules.""" def setUp(self): super(NamespaceModuleTest, self).setUp() if env.PYVERSION < (3, 3): self.skipTest("Python before 3.3 doesn't have namespace packages") def test_explicit_namespace_module(self): self.make_file("main.py", "import namespace_420\n") cov = coverage.Coverage() self.start_import_stop(cov, "main") with self.assertRaisesRegex(CoverageException, r"Module .* has no file"): cov.analysis(sys.modules['namespace_420']) def test_bug_572(self): self.make_file("main.py", "import namespace_420\n") # Use source=namespace_420 to trigger the check that used to fail, # and use source=main so that something is measured. cov = coverage.Coverage(source=["namespace_420", "main"]) with self.assert_warnings(cov, []): self.start_import_stop(cov, "main") cov.report() class OmitIncludeTestsMixin(UsingModulesMixin, CoverageTestMethodsMixin): """Test methods for coverage methods taking include and omit.""" run_in_temp_dir = False def filenames_in(self, summary, filenames): """Assert the `filenames` are in the keys of `summary`.""" for filename in filenames.split(): self.assertIn(filename, summary) def filenames_not_in(self, summary, filenames): """Assert the `filenames` are not in the keys of `summary`.""" for filename in filenames.split(): self.assertNotIn(filename, summary) def test_nothing_specified(self): result = self.coverage_usepkgs() self.filenames_in(result, "p1a p1b p2a p2b othera otherb osa osb") self.filenames_not_in(result, "p1c") # Because there was no source= specified, we don't search for # unexecuted files. def test_include(self): result = self.coverage_usepkgs(include=["*/p1a.py"]) self.filenames_in(result, "p1a") self.filenames_not_in(result, "p1b p1c p2a p2b othera otherb osa osb") def test_include_2(self): result = self.coverage_usepkgs(include=["*a.py"]) self.filenames_in(result, "p1a p2a othera osa") self.filenames_not_in(result, "p1b p1c p2b otherb osb") def test_include_as_string(self): result = self.coverage_usepkgs(include="*a.py") self.filenames_in(result, "p1a p2a othera osa") self.filenames_not_in(result, "p1b p1c p2b otherb osb") def test_omit(self): result = self.coverage_usepkgs(omit=["*/p1a.py"]) self.filenames_in(result, "p1b p2a p2b") self.filenames_not_in(result, "p1a p1c") def test_omit_2(self): result = self.coverage_usepkgs(omit=["*a.py"]) self.filenames_in(result, "p1b p2b otherb osb") self.filenames_not_in(result, "p1a p1c p2a othera osa") def test_omit_as_string(self): result = self.coverage_usepkgs(omit="*a.py") self.filenames_in(result, "p1b p2b otherb osb") self.filenames_not_in(result, "p1a p1c p2a othera osa") def test_omit_and_include(self): result = self.coverage_usepkgs(include=["*/p1*"], omit=["*/p1a.py"]) self.filenames_in(result, "p1b") self.filenames_not_in(result, "p1a p1c p2a p2b") class SourceOmitIncludeTest(OmitIncludeTestsMixin, CoverageTest): """Test using `source`, `omit` and `include` when measuring code.""" def coverage_usepkgs(self, **kwargs): """Run coverage on usepkgs and return the line summary. Arguments are passed to the `coverage.Coverage` constructor. """ cov = coverage.Coverage(**kwargs) cov.start() import usepkgs # pragma: nested # pylint: disable=import-error, unused-variable cov.stop() # pragma: nested data = cov.get_data() summary = data.line_counts() for k, v in list(summary.items()): assert k.endswith(".py") summary[k[:-3]] = v return summary def test_source_include_exclusive(self): cov = coverage.Coverage(source=["pkg1"], include=["pkg2"]) with self.assert_warnings(cov, ["--include is ignored because --source is set"]): cov.start() cov.stop() def test_source_package_as_dir(self): # pkg1 is a directory, since we cd'd into tests/modules in setUp. lines = self.coverage_usepkgs(source=["pkg1"]) self.filenames_in(lines, "p1a p1b") self.filenames_not_in(lines, "p2a p2b othera otherb osa osb") # Because source= was specified, we do search for unexecuted files. self.assertEqual(lines['p1c'], 0) def test_source_package_as_package(self): lines = self.coverage_usepkgs(source=["pkg1.sub"]) self.filenames_not_in(lines, "p2a p2b othera otherb osa osb") # Because source= was specified, we do search for unexecuted files. self.assertEqual(lines['runmod3'], 0) def test_source_package_dotted(self): lines = self.coverage_usepkgs(source=["pkg1.p1b"]) self.filenames_in(lines, "p1b") self.filenames_not_in(lines, "p1a p1c p2a p2b othera otherb osa osb") def test_source_package_part_omitted(self): # https://bitbucket.org/ned/coveragepy/issue/218 # Used to be if you omitted something executed and inside the source, # then after it was executed but not recorded, it would be found in # the search for unexecuted files, and given a score of 0%. # The omit arg is by path, so need to be in the modules directory. self.chdir(self.nice_file(TESTS_DIR, 'modules')) lines = self.coverage_usepkgs(source=["pkg1"], omit=["pkg1/p1b.py"]) self.filenames_in(lines, "p1a") self.filenames_not_in(lines, "p1b") self.assertEqual(lines['p1c'], 0) class ReportIncludeOmitTest(OmitIncludeTestsMixin, CoverageTest): """Tests of the report include/omit functionality.""" def coverage_usepkgs(self, **kwargs): """Try coverage.report().""" cov = coverage.Coverage() cov.start() import usepkgs # pragma: nested # pylint: disable=import-error, unused-variable cov.stop() # pragma: nested report = StringIO() cov.report(file=report, **kwargs) return report.getvalue() class XmlIncludeOmitTest(OmitIncludeTestsMixin, CoverageTest): """Tests of the XML include/omit functionality. This also takes care of the HTML and annotate include/omit, by virtue of the structure of the code. """ def coverage_usepkgs(self, **kwargs): """Try coverage.xml_report().""" cov = coverage.Coverage() cov.start() import usepkgs # pragma: nested # pylint: disable=import-error, unused-variable cov.stop() # pragma: nested cov.xml_report(outfile="-", **kwargs) return self.stdout() class AnalysisTest(CoverageTest): """Test the numerical analysis of results.""" def test_many_missing_branches(self): cov = coverage.Coverage(branch=True) self.make_file("missing.py", """\ def fun1(x): if x == 1: print("one") else: print("not one") print("done") # pragma: nocover def fun2(x): print("x") fun2(3) """) # Import the Python file, executing it. self.start_import_stop(cov, "missing") nums = cov._analyze("missing.py").numbers self.assertEqual(nums.n_files, 1) self.assertEqual(nums.n_statements, 7) self.assertEqual(nums.n_excluded, 1) self.assertEqual(nums.n_missing, 3) self.assertEqual(nums.n_branches, 2) self.assertEqual(nums.n_partial_branches, 0) self.assertEqual(nums.n_missing_branches, 2) class TestRunnerPluginTest(CoverageTest): """Test that the API works properly the way various third-party plugins call it. We don't actually use the plugins, but these tests call the API the same way they do. """ def pretend_to_be_nose_with_cover(self, erase): """This is what the nose --with-cover plugin does.""" cov = coverage.Coverage() self.make_file("no_biggie.py", """\ a = 1 b = 2 if b == 1: c = 4 """) if erase: cov.combine() cov.erase() cov.load() self.start_import_stop(cov, "no_biggie") cov.combine() cov.save() cov.report(["no_biggie.py"], show_missing=True) self.assertEqual(self.stdout(), textwrap.dedent("""\ Name Stmts Miss Cover Missing -------------------------------------------- no_biggie.py 4 1 75% 4 """)) def test_nose_plugin(self): self.pretend_to_be_nose_with_cover(erase=False) def test_nose_plugin_with_erase(self): self.pretend_to_be_nose_with_cover(erase=True) class ReporterDeprecatedAttributeTest(CoverageTest): """Test that Reporter.file_reporters has been deprecated.""" run_in_temp_dir = False def test_reporter_file_reporters(self): rep = Reporter(None, None) with warnings.catch_warnings(record=True) as warns: warnings.simplefilter("always") # Accessing this attribute will raise a DeprecationWarning. rep.file_reporters # pylint: disable=pointless-statement self.assertEqual(len(warns), 1) self.assertTrue(issubclass(warns[0].category, DeprecationWarning)) python-coverage-4.5+dfsg.1.orig/tests/test_concurrency.py0000644000076600000620000004102113233340257021563 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for concurrency libraries.""" import os import random import sys import threading import time from flaky import flaky import coverage from coverage import env from coverage.backward import import_local_file from coverage.files import abs_file from tests.coveragetest import CoverageTest # These libraries aren't always available, we'll skip tests if they aren't. try: import multiprocessing except ImportError: # pragma: only jython multiprocessing = None try: import eventlet except ImportError: eventlet = None try: import gevent except ImportError: gevent = None try: import greenlet except ImportError: # pragma: only jython greenlet = None def measurable_line(l): """Is this a line of code coverage will measure? Not blank, not a comment, and not "else" """ l = l.strip() if not l: return False if l.startswith('#'): return False if l.startswith('else:'): return False if env.JYTHON and l.startswith(('try:', 'except:', 'except ', 'break', 'with ')): # Jython doesn't measure these statements. return False # pragma: only jython return True def line_count(s): """How many measurable lines are in `s`?""" return len(list(filter(measurable_line, s.splitlines()))) def print_simple_annotation(code, linenos): """Print the lines in `code` with X for each line number in `linenos`.""" for lineno, line in enumerate(code.splitlines(), start=1): print(" {0} {1}".format("X" if lineno in linenos else " ", line)) class LineCountTest(CoverageTest): """Test the helpers here.""" run_in_temp_dir = False def test_line_count(self): CODE = """ # Hey there! x = 1 if x: print("hello") else: print("bye") print("done") """ self.assertEqual(line_count(CODE), 5) # The code common to all the concurrency models. SUM_RANGE_Q = """ # Above this will be imports defining queue and threading. class Producer(threading.Thread): def __init__(self, limit, q): threading.Thread.__init__(self) self.limit = limit self.q = q def run(self): for i in range(self.limit): self.q.put(i) self.q.put(None) class Consumer(threading.Thread): def __init__(self, q, qresult): threading.Thread.__init__(self) self.q = q self.qresult = qresult def run(self): sum = 0 while True: i = self.q.get() if i is None: break sum += i self.qresult.put(sum) def sum_range(limit): q = queue.Queue() qresult = queue.Queue() c = Consumer(q, qresult) p = Producer(limit, q) c.start() p.start() p.join() c.join() return qresult.get() # Below this will be something using sum_range. """ PRINT_SUM_RANGE = """ print(sum_range({QLIMIT})) """ # Import the things to use threads. if env.PY2: THREAD = """ import threading import Queue as queue """ else: THREAD = """ import threading import queue """ # Import the things to use eventlet. EVENTLET = """ import eventlet.green.threading as threading import eventlet.queue as queue """ # Import the things to use gevent. GEVENT = """ from gevent import monkey monkey.patch_thread() import threading import gevent.queue as queue """ # Uncomplicated code that doesn't use any of the concurrency stuff, to test # the simple case under each of the regimes. SIMPLE = """ total = 0 for i in range({QLIMIT}): total += i print(total) """ def cant_trace_msg(concurrency, the_module): """What might coverage.py say about a concurrency setting and imported module?""" # In the concurrency choices, "multiprocessing" doesn't count, so remove it. if "multiprocessing" in concurrency: parts = concurrency.split(",") parts.remove("multiprocessing") concurrency = ",".join(parts) if the_module is None: # We don't even have the underlying module installed, we expect # coverage to alert us to this fact. expected_out = ( "Couldn't trace with concurrency=%s, " "the module isn't installed.\n" % concurrency ) elif env.C_TRACER or concurrency == "thread" or concurrency == "": expected_out = None else: expected_out = ( "Can't support concurrency=%s with PyTracer, " "only threads are supported\n" % concurrency ) return expected_out class ConcurrencyTest(CoverageTest): """Tests of the concurrency support in coverage.py.""" QLIMIT = 1000 def try_some_code(self, code, concurrency, the_module, expected_out=None): """Run some concurrency testing code and see that it was all covered. `code` is the Python code to execute. `concurrency` is the name of the concurrency regime to test it under. `the_module` is the imported module that must be available for this to work at all. `expected_out` is the text we expect the code to produce. """ self.make_file("try_it.py", code) cmd = "coverage run --concurrency=%s try_it.py" % concurrency out = self.run_command(cmd) expected_cant_trace = cant_trace_msg(concurrency, the_module) if expected_cant_trace is not None: self.assertEqual(out, expected_cant_trace) else: # We can fully measure the code if we are using the C tracer, which # can support all the concurrency, or if we are using threads. if expected_out is None: expected_out = "%d\n" % (sum(range(self.QLIMIT))) print(code) self.assertEqual(out, expected_out) # Read the coverage file and see that try_it.py has all its lines # executed. data = coverage.CoverageData() data.read_file(".coverage") # If the test fails, it's helpful to see this info: fname = abs_file("try_it.py") linenos = data.lines(fname) print("{0}: {1}".format(len(linenos), linenos)) print_simple_annotation(code, linenos) lines = line_count(code) self.assertEqual(data.line_counts()['try_it.py'], lines) def test_threads(self): code = (THREAD + SUM_RANGE_Q + PRINT_SUM_RANGE).format(QLIMIT=self.QLIMIT) self.try_some_code(code, "thread", threading) def test_threads_simple_code(self): code = SIMPLE.format(QLIMIT=self.QLIMIT) self.try_some_code(code, "thread", threading) def test_eventlet(self): code = (EVENTLET + SUM_RANGE_Q + PRINT_SUM_RANGE).format(QLIMIT=self.QLIMIT) self.try_some_code(code, "eventlet", eventlet) def test_eventlet_simple_code(self): code = SIMPLE.format(QLIMIT=self.QLIMIT) self.try_some_code(code, "eventlet", eventlet) def test_gevent(self): code = (GEVENT + SUM_RANGE_Q + PRINT_SUM_RANGE).format(QLIMIT=self.QLIMIT) self.try_some_code(code, "gevent", gevent) def test_gevent_simple_code(self): code = SIMPLE.format(QLIMIT=self.QLIMIT) self.try_some_code(code, "gevent", gevent) def test_greenlet(self): GREENLET = """\ from greenlet import greenlet def test1(x, y): z = gr2.switch(x+y) print(z) def test2(u): print(u) gr1.switch(42) gr1 = greenlet(test1) gr2 = greenlet(test2) gr1.switch("hello", " world") """ self.try_some_code(GREENLET, "greenlet", greenlet, "hello world\n42\n") def test_greenlet_simple_code(self): code = SIMPLE.format(QLIMIT=self.QLIMIT) self.try_some_code(code, "greenlet", greenlet) def test_bug_330(self): BUG_330 = """\ from weakref import WeakKeyDictionary import eventlet def do(): eventlet.sleep(.01) gts = WeakKeyDictionary() for _ in range(100): gts[eventlet.spawn(do)] = True eventlet.sleep(.005) eventlet.sleep(.1) print(len(gts)) """ self.try_some_code(BUG_330, "eventlet", eventlet, "0\n") SQUARE_OR_CUBE_WORK = """ def work(x): # Use different lines in different subprocesses. if x % 2: y = x*x else: y = x*x*x return y """ SUM_RANGE_WORK = """ def work(x): return sum_range((x+1)*100) """ MULTI_CODE = """ # Above this will be a definition of work(). import multiprocessing import os import time import sys def process_worker_main(args): # Need to pause, or the tasks go too quick, and some processes # in the pool don't get any work, and then don't record data. time.sleep(0.02) ret = work(*args) return os.getpid(), ret if __name__ == "__main__": # pragma: no branch # This if is on a single line so we can get 100% coverage # even if we have no arguments. if len(sys.argv) > 1: multiprocessing.set_start_method(sys.argv[1]) pool = multiprocessing.Pool({NPROCS}) inputs = [(x,) for x in range({UPTO})] outputs = pool.imap_unordered(process_worker_main, inputs) pids = set() total = 0 for pid, sq in outputs: pids.add(pid) total += sq print("%d pids, total = %d" % (len(pids), total)) pool.close() pool.join() """ @flaky(max_runs=10) # Sometimes a test fails due to inherent randomness. Try one more time. class MultiprocessingTest(CoverageTest): """Test support of the multiprocessing module.""" def setUp(self): super(MultiprocessingTest, self).setUp() if not multiprocessing: self.skipTest("No multiprocessing in this Python") # pragma: only jython def try_multiprocessing_code( self, code, expected_out, the_module, concurrency="multiprocessing" ): """Run code using multiprocessing, it should produce `expected_out`.""" self.make_file("multi.py", code) self.make_file(".coveragerc", """\ [run] concurrency = %s source = . """ % concurrency) if env.PYVERSION >= (3, 4): start_methods = ['fork', 'spawn'] else: start_methods = [''] for start_method in start_methods: if start_method and start_method not in multiprocessing.get_all_start_methods(): continue out = self.run_command("coverage run multi.py %s" % (start_method,)) expected_cant_trace = cant_trace_msg(concurrency, the_module) if expected_cant_trace is not None: self.assertEqual(out, expected_cant_trace) else: self.assertEqual(out.rstrip(), expected_out) out = self.run_command("coverage combine") self.assertEqual(out, "") out = self.run_command("coverage report -m") last_line = self.squeezed_lines(out)[-1] self.assertRegex(last_line, r"multi.py \d+ 0 100%") def test_multiprocessing(self): nprocs = 3 upto = 30 code = (SQUARE_OR_CUBE_WORK + MULTI_CODE).format(NPROCS=nprocs, UPTO=upto) total = sum(x*x if x%2 else x*x*x for x in range(upto)) expected_out = "{nprocs} pids, total = {total}".format(nprocs=nprocs, total=total) self.try_multiprocessing_code(code, expected_out, threading) def test_multiprocessing_and_gevent(self): nprocs = 3 upto = 30 code = ( SUM_RANGE_WORK + EVENTLET + SUM_RANGE_Q + MULTI_CODE ).format(NPROCS=nprocs, UPTO=upto) total = sum(sum(range((x + 1) * 100)) for x in range(upto)) expected_out = "{nprocs} pids, total = {total}".format(nprocs=nprocs, total=total) self.try_multiprocessing_code( code, expected_out, eventlet, concurrency="multiprocessing,eventlet" ) def try_multiprocessing_code_with_branching(self, code, expected_out): """Run code using multiprocessing, it should produce `expected_out`.""" self.make_file("multi.py", code) self.make_file("multi.rc", """\ [run] concurrency = multiprocessing branch = True """) if env.PYVERSION >= (3, 4): start_methods = ['fork', 'spawn'] else: start_methods = [''] for start_method in start_methods: if start_method and start_method not in multiprocessing.get_all_start_methods(): continue out = self.run_command("coverage run --rcfile=multi.rc multi.py %s" % (start_method,)) self.assertEqual(out.rstrip(), expected_out) out = self.run_command("coverage combine") self.assertEqual(out, "") out = self.run_command("coverage report -m") last_line = self.squeezed_lines(out)[-1] self.assertRegex(last_line, r"multi.py \d+ 0 \d+ 0 100%") def test_multiprocessing_with_branching(self): nprocs = 3 upto = 30 code = (SQUARE_OR_CUBE_WORK + MULTI_CODE).format(NPROCS=nprocs, UPTO=upto) total = sum(x*x if x%2 else x*x*x for x in range(upto)) expected_out = "{nprocs} pids, total = {total}".format(nprocs=nprocs, total=total) self.try_multiprocessing_code_with_branching(code, expected_out) def test_coverage_stop_in_threads(): has_started_coverage = [] has_stopped_coverage = [] def run_thread(): """Check that coverage is stopping properly in threads.""" deadline = time.time() + 5 ident = threading.currentThread().ident if sys.gettrace() is not None: has_started_coverage.append(ident) while sys.gettrace() is not None: # Wait for coverage to stop time.sleep(0.01) if time.time() > deadline: return has_stopped_coverage.append(ident) cov = coverage.coverage() cov.start() t = threading.Thread(target=run_thread) t.start() time.sleep(0.1) cov.stop() time.sleep(0.1) assert has_started_coverage == [t.ident] assert has_stopped_coverage == [t.ident] t.join() def test_thread_safe_save_data(tmpdir): # Non-regression test for: # https://bitbucket.org/ned/coveragepy/issues/581 # Create some Python modules and put them in the path modules_dir = tmpdir.mkdir('test_modules') module_names = ["m{0:03d}".format(i) for i in range(1000)] for module_name in module_names: modules_dir.join(module_name + ".py").write("def f(): pass\n") # Shared variables for threads should_run = [True] imported = [] old_dir = os.getcwd() os.chdir(modules_dir.strpath) try: # Make sure that all dummy modules can be imported. for module_name in module_names: import_local_file(module_name) def random_load(): """Import modules randomly to stress coverage.""" while should_run[0]: module_name = random.choice(module_names) mod = import_local_file(module_name) mod.f() imported.append(mod) # Spawn some threads with coverage enabled and attempt to read the # results right after stopping coverage collection with the threads # still running. duration = 0.01 for _ in range(3): cov = coverage.coverage() cov.start() threads = [threading.Thread(target=random_load) for _ in range(10)] should_run[0] = True for t in threads: t.start() time.sleep(duration) cov.stop() # The following call used to crash with running background threads. cov.get_data() # Stop the threads should_run[0] = False for t in threads: t.join() if (not imported) and duration < 10: duration *= 2 finally: os.chdir(old_dir) should_run[0] = False python-coverage-4.5+dfsg.1.orig/tests/test_setup.py0000644000076600000620000000307113177624110020374 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests of miscellaneous stuff.""" import sys import coverage from tests.coveragetest import CoverageTest class SetupPyTest(CoverageTest): """Tests of setup.py""" run_in_temp_dir = False def setUp(self): super(SetupPyTest, self).setUp() # Force the most restrictive interpretation. self.set_environ('LC_ALL', 'C') def test_metadata(self): status, output = self.run_command_status( "python setup.py --description --version --url --author" ) self.assertEqual(status, 0) out = output.splitlines() self.assertIn("measurement", out[0]) self.assertEqual(coverage.__version__, out[1]) self.assertIn("bitbucket.org/ned/coveragepy", out[2]) self.assertIn("Ned Batchelder", out[3]) def test_more_metadata(self): # Let's be sure we pick up our own setup.py # CoverageTest restores the original sys.path for us. sys.path.insert(0, '') from setup import setup_args classifiers = setup_args['classifiers'] self.assertGreater(len(classifiers), 7) self.assert_starts_with(classifiers[-1], "Development Status ::") long_description = setup_args['long_description'].splitlines() self.assertGreater(len(long_description), 7) self.assertNotEqual(long_description[0].strip(), "") self.assertNotEqual(long_description[-1].strip(), "") python-coverage-4.5+dfsg.1.orig/tests/plugin2.py0000644000076600000620000000301313220614706017550 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """A file tracer plugin for test_plugins.py to import.""" import os.path import coverage class Plugin(coverage.CoveragePlugin): """A file tracer plugin for testing.""" def file_tracer(self, filename): if "render.py" in filename: return RenderFileTracer() return None def file_reporter(self, filename): return FileReporter(filename) class RenderFileTracer(coverage.FileTracer): """A FileTracer using information from the caller.""" def has_dynamic_source_filename(self): return True def dynamic_source_filename(self, filename, frame): if frame.f_code.co_name != "render": return None source_filename = os.path.abspath(frame.f_locals['filename']) return source_filename def line_number_range(self, frame): lineno = frame.f_locals['linenum'] return lineno, lineno+1 class FileReporter(coverage.FileReporter): """A goofy file reporter.""" def lines(self): # Goofy test arrangement: claim that the file has as many lines as the # number in its name. num = os.path.basename(self.filename).split(".")[0].split("_")[1] return set(range(1, int(num)+1)) def coverage_init(reg, options): # pylint: disable=unused-argument """Called by coverage to initialize the plugins here.""" reg.add_file_tracer(Plugin()) python-coverage-4.5+dfsg.1.orig/tests/test_arcs.py0000644000076600000620000013070213226523175020172 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for coverage.py's arc measurement.""" from tests.coveragetest import CoverageTest import coverage from coverage import env from coverage.files import abs_file class SimpleArcTest(CoverageTest): """Tests for coverage.py's arc measurement.""" def test_simple_sequence(self): self.check_coverage("""\ a = 1 b = 2 """, arcz=".1 12 2.") self.check_coverage("""\ a = 1 b = 3 """, arcz=".1 13 3.") self.check_coverage("""\ a = 2 b = 3 c = 5 """, arcz="-22 23 35 5-2") def test_function_def(self): self.check_coverage("""\ def foo(): a = 2 foo() """, arcz=".1 .2 14 2. 4.") def test_if(self): self.check_coverage("""\ a = 1 if len([]) == 0: a = 3 assert a == 3 """, arcz=".1 12 23 24 34 4.", arcz_missing="24") self.check_coverage("""\ a = 1 if len([]) == 1: a = 3 assert a == 1 """, arcz=".1 12 23 24 34 4.", arcz_missing="23 34") def test_if_else(self): self.check_coverage("""\ if len([]) == 0: a = 2 else: a = 4 assert a == 2 """, arcz=".1 12 25 14 45 5.", arcz_missing="14 45") self.check_coverage("""\ if len([]) == 1: a = 2 else: a = 4 assert a == 4 """, arcz=".1 12 25 14 45 5.", arcz_missing="12 25") def test_compact_if(self): self.check_coverage("""\ a = 1 if len([]) == 0: a = 2 assert a == 2 """, arcz=".1 12 23 3.", ) self.check_coverage("""\ def fn(x): if x % 2: return True return False a = fn(1) assert a == True """, arcz=".1 14 45 5. .2 2. 23 3.", arcz_missing="23 3.") def test_multiline(self): self.check_coverage("""\ a = ( 2 + 3 ) b = \\ 6 """, arcz="-21 15 5-2", ) def test_if_return(self): self.check_coverage("""\ def if_ret(a): if a: return 3 b = 4 return 5 x = if_ret(0) + if_ret(1) assert x == 8 """, arcz=".1 16 67 7. .2 23 24 3. 45 5.", ) def test_dont_confuse_exit_and_else(self): self.check_coverage("""\ def foo(): if foo: a = 3 else: a = 5 return a assert foo() == 3 # 7 """, arcz=".1 17 7. .2 23 36 25 56 6.", arcz_missing="25 56" ) self.check_coverage("""\ def foo(): if foo: a = 3 else: a = 5 foo() # 6 """, arcz=".1 16 6. .2 23 3. 25 5.", arcz_missing="25 5." ) def test_what_is_the_sound_of_no_lines_clapping(self): if env.JYTHON: # Jython reports no lines for an empty file. arcz_missing=".1 1." # pragma: only jython else: # Other Pythons report one line. arcz_missing="" self.check_coverage("""\ # __init__.py """, arcz=".1 1.", arcz_missing=arcz_missing, ) class WithTest(CoverageTest): """Arc-measuring tests involving context managers.""" def test_with(self): self.check_coverage("""\ def example(): with open("test", "w") as f: # exit f.write("") return 1 example() """, arcz=".1 .2 23 34 4. 16 6." ) def test_bug_146(self): # https://bitbucket.org/ned/coveragepy/issue/146 self.check_coverage("""\ for i in range(2): with open("test", "w") as f: print(3) print(4) print(5) """, arcz=".1 12 23 34 41 15 5." ) class LoopArcTest(CoverageTest): """Arc-measuring tests involving loops.""" def test_loop(self): self.check_coverage("""\ for i in range(10): a = i assert a == 9 """, arcz=".1 12 21 13 3.", ) self.check_coverage("""\ a = -1 for i in range(0): a = i assert a == -1 """, arcz=".1 12 23 32 24 4.", arcz_missing="23 32") def test_nested_loop(self): self.check_coverage("""\ for i in range(3): for j in range(3): a = i + j assert a == 4 """, arcz=".1 12 23 32 21 14 4.", ) def test_break(self): self.check_coverage("""\ for i in range(10): a = i break # 3 a = 99 assert a == 0 # 5 """, arcz=".1 12 23 35 15 41 5.", arcz_missing="15 41") def test_continue(self): self.check_coverage("""\ for i in range(10): a = i continue # 3 a = 99 assert a == 9 # 5 """, arcz=".1 12 23 31 15 41 5.", arcz_missing="41") def test_nested_breaks(self): self.check_coverage("""\ for i in range(3): for j in range(3): a = i + j break # 4 if i == 2: break assert a == 2 and i == 2 # 7 """, arcz=".1 12 23 34 45 25 56 51 67 17 7.", arcz_missing="17 25") def test_while_true(self): # With "while 1", the loop knows it's constant. self.check_coverage("""\ a, i = 1, 0 while 1: if i >= 3: a = 4 break i += 1 assert a == 4 and i == 3 """, arcz=".1 12 23 34 45 36 63 57 7.", ) # With "while True", 2.x thinks it's computation, # 3.x thinks it's constant. if env.PY3: arcz = ".1 12 23 34 45 36 63 57 7." else: arcz = ".1 12 23 34 45 36 62 57 7." self.check_coverage("""\ a, i = 1, 0 while True: if i >= 3: a = 4 break i += 1 assert a == 4 and i == 3 """, arcz=arcz, ) def test_zero_coverage_while_loop(self): # https://bitbucket.org/ned/coveragepy/issue/502 self.make_file("main.py", "print('done')") self.make_file("zero.py", """\ def method(self): while True: return 1 """) out = self.run_command("coverage run --branch --source=. main.py") self.assertEqual(out, 'done\n') report = self.report_from_command("coverage report -m") squeezed = self.squeezed_lines(report) self.assertIn("zero.py 3 3 0 0 0% 1-3", squeezed[3]) def test_bug_496_continue_in_constant_while(self): # https://bitbucket.org/ned/coveragepy/issue/496 if env.PY3: arcz = ".1 12 23 34 45 53 46 6." else: arcz = ".1 12 23 34 45 52 46 6." self.check_coverage("""\ up = iter('ta') while True: char = next(up) if char == 't': continue break """, arcz=arcz ) def test_for_if_else_for(self): self.check_coverage("""\ def branches_2(l): if l: for e in l: a = 4 else: a = 6 def branches_3(l): for x in l: if x: for e in l: a = 12 else: a = 14 branches_2([0,1]) branches_3([0,1]) """, arcz= ".1 18 8G GH H. " ".2 23 34 43 26 3. 6. " "-89 9A 9-8 AB BC CB B9 AE E9", arcz_missing="26 6." ) def test_for_else(self): self.check_coverage("""\ def forelse(seq): for n in seq: if n > 5: break else: print('None of the values were greater than 5') print('Done') forelse([1,2]) forelse([1,6]) """, arcz=".1 .2 23 32 34 47 26 67 7. 18 89 9." ) def test_while_else(self): self.check_coverage("""\ def whileelse(seq): while seq: n = seq.pop() if n > 4: break else: n = 99 return n assert whileelse([1, 2]) == 99 assert whileelse([1, 5]) == 5 """, arcz=".1 19 9A A. .2 23 34 45 58 42 27 78 8.", ) def test_confusing_for_loop_bug_175(self): if env.PY3: # Py3 counts the list comp as a separate code object. arcz = ".1 -22 2-2 12 23 34 45 53 3." else: arcz = ".1 12 23 34 45 53 3." self.check_coverage("""\ o = [(1,2), (3,4)] o = [a for a in o] for tup in o: x = tup[0] y = tup[1] """, arcz=arcz, ) if env.PY3: arcz = ".1 12 -22 2-2 23 34 42 2." else: arcz = ".1 12 23 34 42 2." self.check_coverage("""\ o = [(1,2), (3,4)] for tup in [a for a in o]: x = tup[0] y = tup[1] """, arcz=arcz, ) def test_generator_expression(self): # Generator expression: self.check_coverage("""\ o = ((1,2), (3,4)) o = (a for a in o) for tup in o: x = tup[0] y = tup[1] """, arcz=".1 -22 2-2 12 23 34 45 53 3.", ) def test_other_comprehensions(self): if env.PYVERSION < (2, 7): self.skipTest("No set or dict comprehensions before 2.7") # Set comprehension: self.check_coverage("""\ o = ((1,2), (3,4)) o = {a for a in o} for tup in o: x = tup[0] y = tup[1] """, arcz=".1 -22 2-2 12 23 34 45 53 3.", ) # Dict comprehension: self.check_coverage("""\ o = ((1,2), (3,4)) o = {a:1 for a in o} for tup in o: x = tup[0] y = tup[1] """, arcz=".1 -22 2-2 12 23 34 45 53 3.", ) def test_multiline_dict_comp(self): if env.PYVERSION < (2, 7): self.skipTest("No set or dict comprehensions before 2.7") if env.PYVERSION < (3, 5): arcz = "-42 2B B-4 2-4" else: arcz = "-32 2B B-3 2-3" # Multiline dict comp: self.check_coverage("""\ # comment d = \\ { i: str(i) for i in range(9) } x = 11 """, arcz=arcz, ) # Multi dict comp: if env.PYVERSION < (3, 5): arcz = "-42 2F F-4 2-4" else: arcz = "-32 2F F-3 2-3" self.check_coverage("""\ # comment d = \\ { (i, j): str(i+j) for i in range(9) for j in range(13) } x = 15 """, arcz=arcz, ) class ExceptionArcTest(CoverageTest): """Arc-measuring tests involving exception handling.""" def test_try_except(self): self.check_coverage("""\ a, b = 1, 1 try: a = 3 except: b = 5 assert a == 3 and b == 1 """, arcz=".1 12 23 36 45 56 6.", arcz_missing="45 56") self.check_coverage("""\ a, b = 1, 1 try: a = 3 raise Exception("Yikes!") a = 5 except: b = 7 assert a == 3 and b == 7 """, arcz=".1 12 23 34 46 58 67 78 8.", arcz_missing="58", ) def test_hidden_raise(self): self.check_coverage("""\ a, b = 1, 1 def oops(x): if x % 2: raise Exception("odd") try: a = 6 oops(1) a = 8 except: b = 10 assert a == 6 and b == 10 """, arcz=".1 12 -23 34 3-2 4-2 25 56 67 78 8B 9A AB B.", arcz_missing="3-2 78 8B", arcz_unpredicted="79", ) def test_except_with_type(self): self.check_coverage("""\ a, b = 1, 1 def oops(x): if x % 2: raise ValueError("odd") def try_it(x): try: a = 7 oops(x) a = 9 except ValueError: b = 11 return a assert try_it(0) == 9 # C assert try_it(1) == 7 # D """, arcz=".1 12 -23 34 3-2 4-2 25 5D DE E. -56 67 78 89 9C AB BC C-5", arcz_unpredicted="8A", ) def test_try_finally(self): self.check_coverage("""\ a, c = 1, 1 try: a = 3 finally: c = 5 assert a == 3 and c == 5 """, arcz=".1 12 23 35 56 6.", ) self.check_coverage("""\ a, c, d = 1, 1, 1 try: try: a = 4 finally: c = 6 except: d = 8 assert a == 4 and c == 6 and d == 1 # 9 """, arcz=".1 12 23 34 46 78 89 69 9.", arcz_missing="78 89", ) self.check_coverage("""\ a, c, d = 1, 1, 1 try: try: a = 4 raise Exception("Yikes!") a = 6 finally: c = 8 except: d = 10 # A assert a == 4 and c == 8 and d == 10 # B """, arcz=".1 12 23 34 45 58 68 89 8B 9A AB B.", arcz_missing="68 8B", ) def test_finally_in_loop(self): self.check_coverage("""\ a, c, d, i = 1, 1, 1, 99 try: for i in range(5): try: a = 5 if i > 0: raise Exception("Yikes!") a = 8 finally: c = 10 except: d = 12 # C assert a == 5 and c == 10 and d == 12 # D """, arcz=".1 12 23 34 3D 45 56 67 68 7A 8A A3 AB BC CD D.", arcz_missing="3D", ) self.check_coverage("""\ a, c, d, i = 1, 1, 1, 99 try: for i in range(5): try: a = 5 if i > 10: raise Exception("Yikes!") a = 8 finally: c = 10 except: d = 12 # C assert a == 8 and c == 10 and d == 1 # D """, arcz=".1 12 23 34 3D 45 56 67 68 7A 8A A3 AB BC CD D.", arcz_missing="67 7A AB BC CD", ) def test_break_through_finally(self): self.check_coverage("""\ a, c, d, i = 1, 1, 1, 99 try: for i in range(3): try: a = 5 if i > 0: break a = 8 finally: c = 10 except: d = 12 # C assert a == 5 and c == 10 and d == 1 # D """, arcz=".1 12 23 34 3D 45 56 67 68 7A 8A A3 AD BC CD D.", arcz_missing="3D BC CD", ) def test_continue_through_finally(self): self.check_coverage("""\ a, b, c, d, i = 1, 1, 1, 1, 99 try: for i in range(5): try: a = 5 if i > 0: continue b = 8 finally: c = 10 except: d = 12 # C assert (a, b, c, d) == (5, 8, 10, 1) # D """, arcz=".1 12 23 34 3D 45 56 67 68 7A 8A A3 BC CD D.", arcz_missing="BC CD", ) def test_finally_in_loop_bug_92(self): self.check_coverage("""\ for i in range(5): try: j = 3 finally: f = 5 g = 6 h = 7 """, arcz=".1 12 23 35 56 61 17 7.", ) def test_bug_212(self): # "except Exception as e" is crucial here. self.check_coverage("""\ def b(exc): try: while 1: raise Exception(exc) # 4 except Exception as e: if exc != 'expected': raise q = 8 b('expected') try: b('unexpected') # C except: pass """, arcz=".1 .2 1A 23 34 45 56 67 68 7. 8. AB BC C. DE E.", arcz_missing="C.", arcz_unpredicted="CD") def test_except_finally(self): self.check_coverage("""\ a, b, c = 1, 1, 1 try: a = 3 except: b = 5 finally: c = 7 assert a == 3 and b == 1 and c == 7 """, arcz=".1 12 23 45 37 57 78 8.", arcz_missing="45 57") self.check_coverage("""\ a, b, c = 1, 1, 1 def oops(x): if x % 2: raise Exception("odd") try: a = 5 oops(1) a = 7 except: b = 9 finally: c = 11 assert a == 5 and b == 9 and c == 11 """, arcz=".1 12 -23 3-2 24 45 56 67 7B 89 9B BC C.", arcz_missing="67 7B", arcz_unpredicted="68") def test_multiple_except_clauses(self): self.check_coverage("""\ a, b, c = 1, 1, 1 try: a = 3 except ValueError: b = 5 except IndexError: a = 7 finally: c = 9 assert a == 3 and b == 1 and c == 9 """, arcz=".1 12 23 45 46 39 59 67 79 9A A.", arcz_missing="45 59 46 67 79", ) self.check_coverage("""\ a, b, c = 1, 1, 1 try: a = int("xyz") # ValueError except ValueError: b = 5 except IndexError: a = 7 finally: c = 9 assert a == 1 and b == 5 and c == 9 """, arcz=".1 12 23 45 46 39 59 67 79 9A A.", arcz_missing="39 46 67 79", arcz_unpredicted="34", ) self.check_coverage("""\ a, b, c = 1, 1, 1 try: a = [1][3] # IndexError except ValueError: b = 5 except IndexError: a = 7 finally: c = 9 assert a == 7 and b == 1 and c == 9 """, arcz=".1 12 23 45 46 39 59 67 79 9A A.", arcz_missing="39 45 59", arcz_unpredicted="34", ) self.check_coverage("""\ a, b, c = 1, 1, 1 try: try: a = 4/0 # ZeroDivisionError except ValueError: b = 6 except IndexError: a = 8 finally: c = 10 except ZeroDivisionError: pass assert a == 1 and b == 1 and c == 10 """, arcz=".1 12 23 34 4A 56 6A 57 78 8A AD BC CD D.", arcz_missing="4A 56 6A 78 8A AD", arcz_unpredicted="45 7A AB", ) def test_return_finally(self): self.check_coverage("""\ a = [1] def check_token(data): if data: try: return 5 finally: a.append(7) return 8 assert check_token(False) == 8 assert a == [1] assert check_token(True) == 5 assert a == [1, 7] """, arcz=".1 12 29 9A AB BC C-1 -23 34 45 57 7-2 38 8-2", ) def test_except_jump_finally(self): self.check_coverage("""\ def func(x): a = f = g = 2 try: for i in range(4): try: 6/0 except ZeroDivisionError: if x == 'break': a = 9 break elif x == 'continue': a = 12 continue elif x == 'return': a = 15 # F return a, f, g, i # G elif x == 'raise': # H a = 18 # I raise ValueError() # J finally: f = 21 # L except ValueError: # M g = 23 # N return a, f, g, i # O assert func('break') == (9, 21, 2, 0) # Q assert func('continue') == (12, 21, 2, 3) # R assert func('return') == (15, 2, 2, 0) # S assert func('raise') == (18, 21, 23, 0) # T """, arcz= ".1 1Q QR RS ST T. " ".2 23 34 45 56 4O 6L " "78 89 9A AL 8B BC CD DL BE EF FG GL EH HI IJ JL HL " "LO L4 L. LM " "MN NO O.", arcz_missing="6L HL", arcz_unpredicted="67", ) def test_else_jump_finally(self): self.check_coverage("""\ def func(x): a = f = g = 2 try: for i in range(4): try: b = 6 except ZeroDivisionError: pass else: if x == 'break': a = 11 break elif x == 'continue': a = 14 continue elif x == 'return': a = 17 # H return a, f, g, i # I elif x == 'raise': # J a = 20 # K raise ValueError() # L finally: f = 23 # N except ValueError: # O g = 25 # P return a, f, g, i # Q assert func('break') == (11, 23, 2, 0) # S assert func('continue') == (14, 23, 2, 3) # T assert func('return') == (17, 2, 2, 0) # U assert func('raise') == (20, 23, 25, 0) # V """, arcz= ".1 1S ST TU UV V. " ".2 23 34 45 56 6A 78 8N 4Q " "AB BC CN AD DE EF FN DG GH HI IN GJ JK KL LN JN " "NQ N4 N. NO " "OP PQ Q.", arcz_missing="78 8N JN", arcz_unpredicted="", ) class YieldTest(CoverageTest): """Arc tests for generators.""" def test_yield_in_loop(self): self.check_coverage("""\ def gen(inp): for n in inp: yield n list(gen([1,2,3])) """, arcz=".1 .2 23 2. 32 15 5.", ) def test_padded_yield_in_loop(self): self.check_coverage("""\ def gen(inp): i = 2 for n in inp: i = 4 yield n i = 6 i = 7 list(gen([1,2,3])) """, arcz=".1 19 9. .2 23 34 45 56 63 37 7.", ) def test_bug_308(self): self.check_coverage("""\ def run(): for i in range(10): yield lambda: i for f in run(): print(f()) """, arcz=".1 15 56 65 5. .2 23 32 2. -33 3-3", ) self.check_coverage("""\ def run(): yield lambda: 100 for i in range(10): yield lambda: i for f in run(): print(f()) """, arcz=".1 16 67 76 6. .2 23 34 43 3. -22 2-2 -44 4-4", ) self.check_coverage("""\ def run(): yield lambda: 100 # no branch miss for f in run(): print(f()) """, arcz=".1 14 45 54 4. .2 2. -22 2-2", ) def test_bug_324(self): # This code is tricky: the list() call pulls all the values from gen(), # but each of them is a generator itself that is never iterated. As a # result, the generator expression on line 3 is never entered or run. self.check_coverage("""\ def gen(inp): for n in inp: yield (i * 2 for i in range(n)) list(gen([1,2,3])) """, arcz= ".1 15 5. " # The module level ".2 23 32 2. " # The gen() function "-33 3-3", # The generator expression arcz_missing="-33 3-3", ) def test_coroutines(self): self.check_coverage("""\ def double_inputs(): while len([1]): # avoid compiler differences x = yield x *= 2 yield x gen = double_inputs() next(gen) print(gen.send(10)) next(gen) print(gen.send(6)) """, arcz= ".1 17 78 89 9A AB B. " ".2 23 34 45 52 2.", arcz_missing="2.", ) self.assertEqual(self.stdout(), "20\n12\n") def test_yield_from(self): if env.PYVERSION < (3, 3): self.skipTest("Python before 3.3 doesn't have 'yield from'") self.check_coverage("""\ def gen(inp): i = 2 for n in inp: i = 4 yield from range(3) i = 6 i = 7 list(gen([1,2,3])) """, arcz=".1 19 9. .2 23 34 45 56 63 37 7.", arcz_unpredicted="5.", ) def test_abandoned_yield(self): # https://bitbucket.org/ned/coveragepy/issue/440 self.check_coverage("""\ def gen(): print("yup") yield "yielded" print("nope") print(next(gen())) """, lines=[1, 2, 3, 4, 6], missing="4", arcz=".1 16 6. .2 23 34 4.", arcz_missing="34 4.", ) class OptimizedIfTest(CoverageTest): """Tests of if statements being optimized away.""" def test_optimized_away_if_0(self): self.check_coverage("""\ a = 1 if len([2]): c = 3 if 0: # this line isn't in the compiled code. if len([5]): d = 6 else: e = 8 f = 9 """, lines=[1, 2, 3, 8, 9], arcz=".1 12 23 28 38 89 9.", arcz_missing="28", ) def test_optimized_away_if_1(self): self.check_coverage("""\ a = 1 if len([2]): c = 3 if 1: # this line isn't in the compiled code, if len([5]): # but these are. d = 6 else: e = 8 f = 9 """, lines=[1, 2, 3, 5, 6, 9], arcz=".1 12 23 25 35 56 69 59 9.", arcz_missing="25 59", ) self.check_coverage("""\ a = 1 if 1: b = 3 c = 4 d = 5 """, lines=[1, 3, 4, 5], arcz=".1 13 34 45 5.", ) def test_optimized_nested(self): self.check_coverage("""\ a = 1 if 0: if 0: b = 4 else: c = 6 else: if 0: d = 9 else: if 0: e = 11 f = 12 if 0: g = 13 h = 14 i = 15 """, lines=[1, 12, 14, 15], arcz=".1 1C CE EF F.", ) def test_constant_if(self): if env.PYPY: self.skipTest("PyPy doesn't optimize away 'if __debug__:'") # CPython optimizes away "if __debug__:" self.check_coverage("""\ for value in [True, False]: if value: if __debug__: x = 4 else: x = 6 """, arcz=".1 12 24 41 26 61 1.", ) # Before 3.7, no Python optimized away "if not __debug__:" if env.PYVERSION < (3, 7, 0, 'alpha', 4): arcz = ".1 12 23 31 34 41 26 61 1." arcz_missing = "34 41" else: arcz = ".1 12 23 31 26 61 1." arcz_missing = "" self.check_coverage("""\ for value in [True, False]: if value: if not __debug__: x = 4 else: x = 6 """, arcz=arcz, arcz_missing=arcz_missing, ) class MiscArcTest(CoverageTest): """Miscellaneous arc-measuring tests.""" def test_dict_literal(self): if env.PYVERSION < (3, 5): arcz = ".1 19 9." else: # Python 3.5 changed how dict literals are constructed. arcz = "-21 19 9-2" self.check_coverage("""\ d = { 'a': 2, 'b': 3, 'c': { 'd': 5, 'e': 6, } } assert d """, arcz=arcz, ) self.check_coverage("""\ d = \\ { 'a': 2, 'b': 3, 'c': { 'd': 5, 'e': 6, } } assert d """, arcz="-21 19 9-2", ) def test_unpacked_literals(self): if env.PYVERSION < (3, 5): self.skipTest("Don't have unpacked literals until 3.5") self.check_coverage("""\ d = { 'a': 2, 'b': 3, } weird = { **d, **{'c': 7}, 'd': 8, } assert weird['b'] == 3 """, arcz="-21 15 5A A-2" ) self.check_coverage("""\ l = [ 2, 3, ] weird = [ *l, *[7], 8, ] assert weird[1] == 3 """, arcz="-21 15 5A A-2" ) def test_pathologically_long_code_object(self): if env.JYTHON: self.skipTest("Bytecode concerns are irrelevant on Jython") # https://bitbucket.org/ned/coveragepy/issue/359 # The structure of this file is such that an EXTENDED_ARG bytecode is # needed to encode the jump at the end. We weren't interpreting those # opcodes. # Note that we no longer interpret bytecode at all, but it couldn't # hurt to keep the test... code = """\ data = [ """ + "".join("""\ [ {i}, {i}, {i}, {i}, {i}, {i}, {i}, {i}, {i}, {i}], """.format(i=i) for i in range(2000) ) + """\ ] print(len(data)) """ self.check_coverage( code, arcs=[(-3, 1), (1, 4004), (4004, -3)], arcs_missing=[], arcs_unpredicted=[], ) def test_partial_generators(self): # https://bitbucket.org/ned/coveragepy/issues/475/generator-expression-is-marked-as-not # Line 2 is executed completely. # Line 3 is started but not finished, because zip ends when #2 ends. # Line 4 is never started. cov = self.check_coverage("""\ def f(a, b): c = (i for i in a) # 2 d = (j for j in b) # 3 e = (k for k in b) # 4 return dict(zip(c, d)) f(['a', 'b'], [1, 2]) """, arcz=".1 17 7. .2 23 34 45 5. -22 2-2 -33 3-3 -44 4-4", arcz_missing="3-3 -44 4-4", ) # ugh, unexposed methods?? filename = self.last_module_name + ".py" fr = cov._get_file_reporter(filename) arcs_executed = cov._analyze(filename).arcs_executed() self.assertEqual( fr.missing_arc_description(3, -3, arcs_executed), "line 3 didn't finish the generator expression on line 3" ) self.assertEqual( fr.missing_arc_description(4, -4, arcs_executed), "line 4 didn't run the generator expression on line 4" ) class DecoratorArcTest(CoverageTest): """Tests of arcs with decorators.""" def test_function_decorator(self): self.check_coverage("""\ def decorator(arg): def _dec(f): return f return _dec @decorator(6) @decorator( len([8]), ) def my_function( a=len([11]), ): x = 13 a = 14 my_function() """, arcz= ".1 16 67 7A AE EF F. " # main line ".2 24 4. -23 3-2 " # decorators "-6D D-6 ", # my_function ) def test_class_decorator(self): self.check_coverage("""\ def decorator(arg): def _dec(c): return c return _dec @decorator(6) @decorator( len([8]), ) class MyObject( object ): X = 13 a = 14 """, arcz= ".1 16 67 6D 7A AE E. " # main line ".2 24 4. -23 3-2 " # decorators "-66 D-6 ", # MyObject ) def test_bug_466(self): # A bad interaction between decorators and multi-line list assignments, # believe it or not...! self.check_coverage("""\ class Parser(object): @classmethod def parse(cls): formats = [ 5 ] return None Parser.parse() """, arcz=".1 1A A. 13 3. -35 58 8-3", ) self.check_coverage("""\ class Parser(object): @classmethod def parse(cls): formats = [ 6, ] return None Parser.parse() """, arcz=".1 1A A. 13 3. -35 58 8-3", ) class LambdaArcTest(CoverageTest): """Tests of lambdas""" def test_multiline_lambda(self): self.check_coverage("""\ fn = (lambda x: x + 2 ) assert fn(4) == 6 """, arcz=".1 14 4-1 1-1", ) self.check_coverage("""\ fn = \\ ( lambda x: x + 8 ) assert fn(10) == 18 """, arcz="-42 2A A-4 2-4", ) def test_unused_lambdas_are_confusing_bug_90(self): self.check_coverage("""\ a = 1 fn = lambda x: x b = 3 """, arcz=".1 12 -22 2-2 23 3.", arcz_missing="-22 2-2", ) def test_raise_with_lambda_looks_like_partial_branch(self): self.check_coverage("""\ def ouch(fn): 2/0 a = b = c = d = 3 try: a = ouch(lambda: 5) if a: b = 7 except ZeroDivisionError: c = 9 d = 10 assert (a, b, c, d) == (3, 3, 9, 10) """, lines=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], missing="6-7", arcz=".1 13 34 45 56 67 6A 7A 89 9A AB B. .2 2. -55 5-5", arcz_missing="56 67 6A 7A -55 5-5", arcz_unpredicted="58", ) def test_lambda_in_dict(self): self.check_coverage("""\ x = 1 x = 2 d = { 4: lambda: [], 5: lambda: [], 6: lambda: [], 7: lambda: [], } for k, v in d.items(): # 10 if k & 1: v() """, arcz=".1 12 23 3A AB BC BA CA A. -43 -53 -63 -73 3-4 3-5 3-6 3-7", arcz_missing="-43 3-4 -63 3-6", arcz_unpredicted="", ) class AsyncTest(CoverageTest): """Tests of the new async and await keywords in Python 3.5""" def setUp(self): if env.PYVERSION < (3, 5): self.skipTest("Async features are new in Python 3.5") super(AsyncTest, self).setUp() def test_async(self): self.check_coverage("""\ import asyncio async def compute(x, y): # 3 print("Compute %s + %s ..." % (x, y)) await asyncio.sleep(0.001) return x + y # 6 async def print_sum(x, y): # 8 result = (0 + await compute(x, y) # A ) print("%s + %s = %s" % (x, y, result)) loop = asyncio.get_event_loop() # E loop.run_until_complete(print_sum(1, 2)) loop.close() # G """, arcz= ".1 13 38 8E EF FG G. " "-34 45 56 6-3 " "-89 9C C-8", arcz_unpredicted="5-3 9-8", ) self.assertEqual(self.stdout(), "Compute 1 + 2 ...\n1 + 2 = 3\n") def test_async_for(self): self.check_coverage("""\ import asyncio class AsyncIteratorWrapper: # 3 def __init__(self, obj): # 4 self._it = iter(obj) def __aiter__(self): # 7 return self async def __anext__(self): # A try: return next(self._it) except StopIteration: raise StopAsyncIteration async def doit(): # G async for letter in AsyncIteratorWrapper("abc"): print(letter) print(".") loop = asyncio.get_event_loop() # L loop.run_until_complete(doit()) loop.close() """, arcz= ".1 13 3G GL LM MN N. " # module main line "-33 34 47 7A A-3 " # class definition "-GH HI IH HJ J-G " # doit "-45 5-4 " # __init__ "-78 8-7 " # __aiter__ "-AB BC C-A DE E-A ", # __anext__ arcz_unpredicted="CD", ) self.assertEqual(self.stdout(), "a\nb\nc\n.\n") def test_async_with(self): self.check_coverage("""\ async def go(): async with x: pass """, arcz=".1 1. .2 23 3.", arcz_missing=".2 23 3.", ) class ExcludeTest(CoverageTest): """Tests of exclusions to indicate known partial branches.""" def test_default(self): # A number of forms of pragma comment are accepted. self.check_coverage("""\ a = 1 if a: #pragma: no branch b = 3 c = 4 if c: # pragma NOBRANCH d = 6 e = 7 if e:#\tpragma:\tno branch f = 9 """, [1,2,3,4,5,6,7,8,9], arcz=".1 12 23 24 34 45 56 57 67 78 89 9. 8.", ) def test_custom_pragmas(self): self.check_coverage("""\ a = 1 while a: # [only some] c = 3 break assert c == 5-2 """, [1,2,3,4,5], partials=["only some"], arcz=".1 12 23 34 45 25 5.", ) class LineDataTest(CoverageTest): """Tests that line_data gives us what we expect.""" def test_branch(self): cov = coverage.Coverage(branch=True) self.make_file("fun1.py", """\ def fun1(x): if x == 1: return fun1(3) """) self.start_import_stop(cov, "fun1") data = cov.get_data() fun1_lines = data.lines(abs_file("fun1.py")) self.assertCountEqual(fun1_lines, [1, 2, 5]) python-coverage-4.5+dfsg.1.orig/tests/stress_phystoken.tok0000644000076600000620000000211513146037571021774 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Here's some random Python so that test_tokenize_myself will have some # stressful stuff to try. This file is .tok instead of .py so pylint won't # complain about it, check_eol won't look at it, etc. first_back = """\ hey there! """ other_back = """ hey \ there """ lots_of_back = """\ hey \ there """ # This next line is supposed to have trailing whitespace: fake_back = """\ ouch """ # Lots of difficulty happens with code like: # # fake_back = """\ # ouch # """ # # Ugh, the edge cases... # What about a comment like this\ "what's this string doing here?" class C(object): def there(): this = 5 + \ 7 that = \ "a continued line" cont1 = "one line of text" + \ "another line of text" a_long_string = \ "part 1" \ "2" \ "3 is longer" def hello(): print("Hello world!") hello() python-coverage-4.5+dfsg.1.orig/tests/farm/0000755000076600000620000000000013235414514016547 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/0000755000076600000620000000000013235414514017513 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_isolatin1/0000755000076600000620000000000013235414515022424 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_isolatin1/isolatin1_py.html0000644000076600000620000000775113146037571025743 0ustar staff Coverage for isolatin1.py: 100%

Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

# -*- coding: iso8859-1 -*- 

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

# A Python source file in another encoding. 

 

math = "3×4 = 12, ÷2 = 6±0" 

assert len(math) == 18 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_isolatin1/index.html0000644000076600000620000000564513146037571024437 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x c   change column sorting

Module statements missing excluded coverage
Total 2 0 0 100%
isolatin1.py 2 0 0 100%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_2/0000755000076600000620000000000013235414515021712 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_2/m3_py.html0000644000076600000620000000660113146037571023636 0ustar staff Coverage for m3.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

m3a = 1 

m3b = 2 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_2/index.html0000644000076600000620000000675213146037571023725 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x c   change column sorting

Module statements missing excluded coverage
Total 12 0 0 100%
m2.py 2 0 0 100%
m3.py 2 0 0 100%
main.py 8 0 0 100%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_2/main_py.html0000644000076600000620000001206013146037571024237 0ustar staff Coverage for main.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

import m1 

import m2 

import m3 

 

a = 5 

b = 6 

 

assert m1.m1a == 1 

assert m2.m2a == 1 

assert m3.m3a == 1 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_2/m2_py.html0000644000076600000620000000660113146037571023635 0ustar staff Coverage for m2.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

m2a = 1 

m2b = 2 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/othersrc/0000755000076600000620000000000013235414515021345 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/othersrc/other.py0000644000076600000620000000042213146037571023042 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # A file in another directory. We're checking that it ends up in the # HTML report. print("This is the other src!") python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_other/0000755000076600000620000000000013235414515021642 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_other/here_py.html0000644000076600000620000001062113146037571024167 0ustar staff Coverage for here.py: 75%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

# A test file for HTML reporting by coverage.py. 

 

import other 

 

if 1 < 2: 

    h = 3 

else: 

    h = 4 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_other/index.html0000644000076600000620000000645213146037571023652 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x c   change column sorting

Module statements missing excluded coverage
Total 5 1 0 80%
/Users/ned/coverage/trunk/tests/farm/html/othersrc/other.py 1 0 0 100%
here.py 4 1 0 75%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_other/blah_blah_other_py.html0000644000076600000620000000744213146037571026350 0ustar staff Coverage for /Users/ned/coverage/trunk/tests/farm/html/othersrc/other.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

# A file in another directory.  We're checking that it ends up in the 

# HTML report. 

 

print("This is the other src!") 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_b_branch/0000755000076600000620000000000013235414515022257 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_b_branch/b_py.html0000644000076600000620000002222713146037571024107 0ustar staff Coverage for b.py: 70%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

# A test file for HTML reporting by coverage.py. 

 

def one(x): 

    # This will be a branch that misses the else. 

11    if x < 2: 

        a = 3 

    else: 

        a = 4 

 

one(1) 

 

def two(x): 

    # A missed else that branches to "exit" 

exit    if x: 

        a = 5 

 

two(1) 

 

def three(): 

    try: 

        # This if has two branches, *neither* one taken. 

26   28        if name_error_this_variable_doesnt_exist: 

            a = 1 

        else: 

            a = 2 

    except: 

        pass 

 

three() 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_b_branch/index.html0000644000076600000620000000627013146037571024265 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x b p c   change column sorting

Module statements missing excluded branches partial coverage
Total 17 3 0 6 4 70%
b 17 3 0 6 4 70%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_5/0000755000076600000620000000000013235414515021715 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_5/m1_py.html0000644000076600000620000000660113146037571023637 0ustar staff Coverage for m1.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

m1a = 1 

m1b = 2 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_5/index.html0000644000076600000620000000630413146037571023721 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x c   change column sorting

Module statements missing excluded coverage
Total 10 0 0 100%
m1.py 2 0 0 100%
main.py 8 0 0 100%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_5/main_py.html0000644000076600000620000001206013146037571024242 0ustar staff Coverage for main.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

import m1 

import m2 

import m3 

 

a = 5 

b = 6 

 

assert m1.m1a == 1 

assert m2.m2a == 1 

assert m3.m3a == 1 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_a/0000755000076600000620000000000013235414515020741 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_a/index.html0000644000076600000620000000562213146037571022747 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x c   change column sorting

Module statements missing excluded coverage
Total 3 1 0 67%
a.py 3 1 0 67%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_a/a_py.html0000644000076600000620000001042313146037571022563 0ustar staff Coverage for a.py: 67%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

# A test file for HTML reporting by coverage.py. 

 

if 1 < 2: 

    # Needed a < to look at HTML entities. 

    a = 3 

else: 

    a = 4 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_partial/0000755000076600000620000000000013235414515022155 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_partial/partial_py.html0000644000076600000620000001417713146037571025225 0ustar staff Coverage for partial.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

# partial branches 

 

a = 3 

 

while True: 

    break 

 

while 1: 

    break 

 

while a:        # pragma: no branch 

    break 

 

if 0: 

    never_happen() 

 

if 1: 

    a = 13 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_partial/index.html0000644000076600000620000000637413146037571024170 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x b p c   change column sorting

Module statements missing excluded branches partial coverage
Total 8 0 0 4 0 100%
partial.py 8 0 0 4 0 100%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/0000755000076600000620000000000013235414515020303 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/src/bom.py0000644000076600000620000000064713146037571021445 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # A Python source file in utf-8, with BOM. math = "3×4 = 12, ÷2 = 6±0" import sys if sys.version_info >= (3, 0): assert len(math) == 18 assert len(math.encode('utf-8')) == 21 else: assert len(math) == 21 assert len(math.decode('utf-8')) == 18 python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/a.py0000644000076600000620000000043513146037571021103 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # A test file for HTML reporting by coverage.py. if 1 < 2: # Needed a < to look at HTML entities. a = 3 else: a = 4 python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/m1.py0000644000076600000620000000025413146037571021177 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt m1a = 1 m1b = 2 python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/partial.ini0000644000076600000620000000034313146037571022444 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt [run] branch = True [report] exclude_lines = raise AssertionError python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/b.py0000644000076600000620000000116713146037571021107 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # A test file for HTML reporting by coverage.py. def one(x): # This will be a branch that misses the else. if x < 2: a = 3 else: a = 4 one(1) def two(x): # A missed else that branches to "exit" if x: a = 5 two(1) def three(): try: # This if has two branches, *neither* one taken. if name_error_this_variable_doesnt_exist: a = 1 else: a = 2 except: pass three() python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/m3.py0000644000076600000620000000025413146037571021201 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt m3a = 1 m3b = 2 python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/omit4.ini0000644000076600000620000000026213146037571022044 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt [report] omit = m2.py python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/tabbed.py0000644000076600000620000000047313146037571022106 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # This file should have tabs. x = 1 if x: a = "Tabbed" # Aligned comments if x: # look nice b = "No spaces" # when they c = "Done" # line up. python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/isolatin1.py0000644000076600000620000000043013146037571022561 0ustar staff# -*- coding: iso8859-1 -*- # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # A Python source file in another encoding. math = "34 = 12, 2 = 60" assert len(math) == 18 python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/y.py0000644000076600000620000000045513146037571021135 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # A test file for XML reporting by coverage.py. def choice(x): if x < 2: return 3 else: return 4 assert choice(1) == 3 python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/main.py0000644000076600000620000000040113146037571021600 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt import m1 import m2 import m3 a = 5 b = 6 assert m1.m1a == 1 assert m2.m2a == 1 assert m3.m3a == 1 python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/run_a_xml_2.ini0000644000076600000620000000034713146037571023221 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Put all the XML output in xml_2 [xml] output = ../out/xml_2/coverage.xml python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/m2.py0000644000076600000620000000025413146037571021200 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt m2a = 1 m2b = 2 python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/omit5.ini0000644000076600000620000000040313146037571022042 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt [report] omit = fooey gooey, m[23]*, kablooey helloworld [html] directory = ../out/omit_5 python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/here.py0000644000076600000620000000040013146037571021576 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # A test file for HTML reporting by coverage.py. import other if 1 < 2: h = 3 else: h = 4 python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/extra.css0000644000076600000620000000007013146037571022141 0ustar staff/* Doesn't matter what goes in here, it gets copied. */ python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/partial.py0000644000076600000620000000057613146037571022325 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # partial branches and excluded lines a = 6 while True: break while 1: break while a: # pragma: no branch break if 0: never_happen() if 1: a = 21 if a == 23: raise AssertionError("Can't") python-coverage-4.5+dfsg.1.orig/tests/farm/html/src/unicode.py0000644000076600000620000000044713146037571022314 0ustar staff# -*- coding: utf-8 -*- # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # A Python source file with exotic characters. upside_down = "ʎd˙ǝbɐɹǝʌoɔ" surrogate = "db40,dd00: x󠄀" python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_3/0000755000076600000620000000000013235414515021713 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_3/m3_py.html0000644000076600000620000000660113146037571023637 0ustar staff Coverage for m3.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

m3a = 1 

m3b = 2 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_3/index.html0000644000076600000620000000630413146037571023717 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x c   change column sorting

Module statements missing excluded coverage
Total 10 0 0 100%
m3.py 2 0 0 100%
main.py 8 0 0 100%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_3/main_py.html0000644000076600000620000001206013146037571024240 0ustar staff Coverage for main.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

import m1 

import m2 

import m3 

 

a = 5 

b = 6 

 

assert m1.m1a == 1 

assert m2.m2a == 1 

assert m3.m3a == 1 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_bom/0000755000076600000620000000000013235414515021276 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_bom/bom_py.html0000644000076600000620000001276213146037571023465 0ustar staff Coverage for bom: 71%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

# A python source file in utf-8, with BOM 

math = "3×4 = 12, ÷2 = 6±0" 

 

import sys 

 

if sys.version_info >= (3, 0): 

    assert len(math) == 18 

    assert len(math.encode('utf-8')) == 21 

else: 

    assert len(math) == 21 

    assert len(math.decode('utf-8')) == 18 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_bom/index.html0000644000076600000620000000553713146037571023311 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x c   change column sorting

Module statements missing excluded coverage
Total 7 2 0 71%
bom 7 2 0 71%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_styled/0000755000076600000620000000000013235414515022025 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_styled/style.css0000644000076600000620000001470713146037571023714 0ustar staff/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ /* For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt */ /* CSS styles for coverage.py. */ /* Page-wide styles */ html, body, h1, h2, h3, p, table, td, th { margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; } /* Set baseline grid to 16 pt. */ body { font-family: georgia, serif; font-size: 1em; } html>body { font-size: 16px; } /* Set base font size to 12/16 */ p { font-size: .75em; /* 12/16 */ line-height: 1.33333333em; /* 16/12 */ } table { border-collapse: collapse; } td { vertical-align: top; } table tr.hidden { display: none !important; } p#no_rows { display: none; font-size: 1.2em; } a.nav { text-decoration: none; color: inherit; } a.nav:hover { text-decoration: underline; color: inherit; } /* Page structure */ #header { background: #f8f8f8; width: 100%; border-bottom: 1px solid #eee; } #source { padding: 1em; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } .indexfile #footer { margin: 1em 3em; } .pyfile #footer { margin: 1em 1em; } #footer .content { padding: 0; font-size: 85%; font-family: verdana, sans-serif; color: #666666; font-style: italic; } #index { margin: 1em 0 0 3em; } /* Header styles */ #header .content { padding: 1em 3em; } h1 { font-size: 1.25em; display: inline-block; } #filter_container { display: inline-block; float: right; margin: 0 2em 0 0; } #filter_container input { width: 10em; } h2.stats { margin-top: .5em; font-size: 1em; } .stats span { border: 1px solid; padding: .1em .25em; margin: 0 .1em; cursor: pointer; border-color: #999 #ccc #ccc #999; } .stats span.hide_run, .stats span.hide_exc, .stats span.hide_mis, .stats span.hide_par, .stats span.par.hide_run.hide_par { border-color: #ccc #999 #999 #ccc; } .stats span.par.hide_run { border-color: #999 #ccc #ccc #999; } .stats span.run { background: #ddffdd; } .stats span.exc { background: #eeeeee; } .stats span.mis { background: #ffdddd; } .stats span.hide_run { background: #eeffee; } .stats span.hide_exc { background: #f5f5f5; } .stats span.hide_mis { background: #ffeeee; } .stats span.par { background: #ffffaa; } .stats span.hide_par { background: #ffffcc; } /* Help panel */ #keyboard_icon { float: right; margin: 5px; cursor: pointer; } .help_panel { position: absolute; background: #ffffcc; padding: .5em; border: 1px solid #883; display: none; } .indexfile .help_panel { width: 20em; height: 4em; } .pyfile .help_panel { width: 16em; height: 8em; } .help_panel .legend { font-style: italic; margin-bottom: 1em; } #panel_icon { float: right; cursor: pointer; } .keyhelp { margin: .75em; } .keyhelp .key { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: monospace; font-weight: bold; background: #eee; } /* Source file styles */ .linenos p { text-align: right; margin: 0; padding: 0 .5em; color: #999999; font-family: verdana, sans-serif; font-size: .625em; /* 10/16 */ line-height: 1.6em; /* 16/10 */ } .linenos p.highlight { background: #ffdd00; } .linenos p a { text-decoration: none; color: #999999; } .linenos p a:hover { text-decoration: underline; color: #999999; } td.text { width: 100%; } .text p { margin: 0; padding: 0 0 0 .5em; border-left: 2px solid #ffffff; white-space: pre; position: relative; } .text p.mis { background: #ffdddd; border-left: 2px solid #ff0000; } .text p.run, .text p.run.hide_par { background: #ddffdd; border-left: 2px solid #00ff00; } .text p.exc { background: #eeeeee; border-left: 2px solid #808080; } .text p.par, .text p.par.hide_run { background: #ffffaa; border-left: 2px solid #eeee99; } .text p.hide_run, .text p.hide_exc, .text p.hide_mis, .text p.hide_par, .text p.hide_run.hide_par { background: inherit; } .text span.annotate { font-family: georgia; color: #666; float: right; padding-right: .5em; } .text p.hide_par span.annotate { display: none; } .text span.annotate.long { display: none; } .text p:hover span.annotate.long { display: block; max-width: 50%; white-space: normal; float: right; position: absolute; top: 1.75em; right: 1em; width: 30em; height: auto; color: #333; background: #ffffcc; border: 1px solid #888; padding: .25em .5em; z-index: 999; border-radius: .2em; box-shadow: #cccccc .2em .2em .2em; } /* Syntax coloring */ .text .com { color: green; font-style: italic; line-height: 1px; } .text .key { font-weight: bold; line-height: 1px; } .text .str { color: #000080; } /* index styles */ #index td, #index th { text-align: right; width: 5em; padding: .25em .5em; border-bottom: 1px solid #eee; } #index th { font-style: italic; color: #333; border-bottom: 1px solid #ccc; cursor: pointer; } #index th:hover { background: #eee; border-bottom: 1px solid #999; } #index td.left, #index th.left { padding-left: 0; } #index td.right, #index th.right { padding-right: 0; } #index th.headerSortDown, #index th.headerSortUp { border-bottom: 1px solid #000; } #index td.name, #index th.name { text-align: left; width: auto; } #index td.name a { text-decoration: none; color: #000; } #index tr.total, #index tr.total_dynamic { } #index tr.total td, #index tr.total_dynamic td { font-weight: bold; border-top: 1px solid #ccc; border-bottom: none; } #index tr.file:hover { background: #eeeeee; } #index tr.file:hover td.name { text-decoration: underline; color: #000; } /* scroll marker styles */ #scroll_marker { position: fixed; right: 0; top: 0; width: 16px; height: 100%; background: white; border-left: 1px solid #eee; } #scroll_marker .marker { background: #ffdddd; position: absolute; min-height: 3px; width: 100%; } python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_styled/index.html0000644000076600000620000000573013146037571024033 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x c   change column sorting

Module statements missing excluded coverage
Total 3 1 0 67%
a.py 3 1 0 67%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_styled/extra.css0000644000076600000620000000007013146037571023663 0ustar staff/* Doesn't matter what goes in here, it gets copied. */ python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_styled/a_py.html0000644000076600000620000001053113146037571023647 0ustar staff Coverage for a.py: 67%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

# A test file for HTML reporting by coverage.py. 

 

if 1 < 2: 

    # Needed a < to look at HTML entities. 

    a = 3 

else: 

    a = 4 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_1/0000755000076600000620000000000013235414515021711 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_1/m1_py.html0000644000076600000620000000660113146037571023633 0ustar staff Coverage for m1.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

m1a = 1 

m1b = 2 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_1/m3_py.html0000644000076600000620000000660113146037571023635 0ustar staff Coverage for m3.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

m3a = 1 

m3b = 2 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_1/index.html0000644000076600000620000000742013146037571023715 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x c   change column sorting

Module statements missing excluded coverage
Total 14 0 0 100%
m1.py 2 0 0 100%
m2.py 2 0 0 100%
m3.py 2 0 0 100%
main.py 8 0 0 100%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_1/main_py.html0000644000076600000620000001206013146037571024236 0ustar staff Coverage for main.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

import m1 

import m2 

import m3 

 

a = 5 

b = 6 

 

assert m1.m1a == 1 

assert m2.m2a == 1 

assert m3.m3a == 1 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_1/m2_py.html0000644000076600000620000000660113146037571023634 0ustar staff Coverage for m2.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

m2a = 1 

m2b = 2 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_4/0000755000076600000620000000000013235414515021714 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_4/m1_py.html0000644000076600000620000000660113146037571023636 0ustar staff Coverage for m1.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

m1a = 1 

m1b = 2 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_4/m3_py.html0000644000076600000620000000660113146037571023640 0ustar staff Coverage for m3.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

m3a = 1 

m3b = 2 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_4/index.html0000644000076600000620000000675213146037571023727 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x c   change column sorting

Module statements missing excluded coverage
Total 12 0 0 100%
m1.py 2 0 0 100%
m3.py 2 0 0 100%
main.py 8 0 0 100%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_omit_4/main_py.html0000644000076600000620000001206013146037571024241 0ustar staff Coverage for main.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

import m1 

import m2 

import m3 

 

a = 5 

b = 6 

 

assert m1.m1a == 1 

assert m2.m2a == 1 

assert m3.m3a == 1 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_x_xml/0000755000076600000620000000000013235414515021650 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_x_xml/coverage.xml0000644000076600000620000000155013173515667024201 0ustar staff /Users/ned/coverage/trunk/tests/farm/html/src python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_unicode/0000755000076600000620000000000013235414515022147 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_unicode/unicode_py.html0000644000076600000620000000766213146037571025212 0ustar staff Coverage for unicode.py: 100%
Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

# -*- coding: utf-8 -*- 

# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 

 

# A Python source file with exotic characters. 

 

upside_down = "ʎd˙ǝbɐɹǝʌoɔ" 

surrogate = "db40,dd00: x󠄀" 

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_unicode/index.html0000644000076600000620000000564113146037571024156 0ustar staff Coverage report
Hide keyboard shortcuts

Hot-keys on this page

n s m x c   change column sorting

Module statements missing excluded coverage
Total 2 0 0 100%
unicode.py 2 0 0 100%

No items found using the specified filter.

python-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_y_xml_branch/0000755000076600000620000000000013235414515023166 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/html/gold_y_xml_branch/coverage.xml0000644000076600000620000000175513173515667025526 0ustar staff /Users/ned/coverage/trunk/tests/farm/html/src python-coverage-4.5+dfsg.1.orig/tests/farm/run/0000755000076600000620000000000013235414515017354 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/run/run_xxx.py0000644000076600000620000000103313173515667021451 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt copy("src", "out_xxx") run(""" coverage run xxx coverage report """, rundir="out_xxx", outfile="stdout.txt") contains("out_xxx/stdout.txt", "xxx: 3 4 0 7", "\nxxx ", # The reporting line for xxx " 7 1 86%" # The reporting data for xxx ) doesnt_contain("out_xxx/stdout.txt", "No such file or directory") clean("out_xxx") python-coverage-4.5+dfsg.1.orig/tests/farm/run/run_timid.py0000644000076600000620000000320013173515667021726 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Test that the --timid command line argument properly swaps the tracer # function for a simpler one. # # This is complicated by the fact that the tests are run twice for each # version: once with a compiled C-based trace function, and once without # it, to also test the Python trace function. So this test has to examine # an environment variable set in igor.py to know whether to expect to see # the C trace function or not. import os # When meta-coverage testing, this test doesn't work, because it finds # coverage.py's own trace function. if os.environ.get('COVERAGE_COVERAGE', ''): skip("Can't test timid during coverage measurement.") copy("src", "out_timid") run(""" python showtrace.py none coverage run showtrace.py regular coverage run --timid showtrace.py timid """, rundir="out_timid", outfile="showtraceout.txt") # When running without coverage, no trace function # When running timidly, the trace function is always Python. contains("out_timid/showtraceout.txt", "none None", "timid PyTracer", ) if os.environ.get('COVERAGE_TEST_TRACER', 'c') == 'c': # If the C trace function is being tested, then regular running should have # the C function, which registers itself as f_trace. contains("out_timid/showtraceout.txt", "regular CTracer") else: # If the Python trace function is being tested, then regular running will # also show the Python function. contains("out_timid/showtraceout.txt", "regular PyTracer") clean("out_timid") python-coverage-4.5+dfsg.1.orig/tests/farm/run/run_chdir.py0000644000076600000620000000073013173515667021716 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt copy("src", "out_chdir") run(""" coverage run chdir.py coverage report """, rundir="out_chdir", outfile="stdout.txt") contains("out_chdir/stdout.txt", "Line One", "Line Two", "chdir" ) doesnt_contain("out_chdir/stdout.txt", "No such file or directory") clean("out_chdir") python-coverage-4.5+dfsg.1.orig/tests/farm/run/src/0000755000076600000620000000000013235414516020144 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/run/src/chdir.py0000644000076600000620000000033513146037571021613 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt import os print("Line One") os.chdir("subdir") print("Line Two") python-coverage-4.5+dfsg.1.orig/tests/farm/run/src/showtrace.py0000644000076600000620000000171113146037571022520 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Show the current frame's trace function, so that we can test what the # command-line options do to the trace function used. import sys # Show what the trace function is. If a C-based function is used, then f_trace # may be None. trace_fn = sys._getframe(0).f_trace if trace_fn is None: trace_name = "None" else: # Get the name of the tracer class. Py3k has a different way to get it. try: trace_name = trace_fn.im_class.__name__ except AttributeError: try: trace_name = trace_fn.__self__.__class__.__name__ except AttributeError: # A C-based function could also manifest as an f_trace value # which doesn't have im_class or __self__. trace_name = trace_fn.__class__.__name__ print("%s %s" % (sys.argv[1], trace_name)) python-coverage-4.5+dfsg.1.orig/tests/farm/run/src/xxx0000644000076600000620000000051113146037571020716 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # This is a python file though it doesn't look like it, like a main script. a = b = c = d = 0 a = 3 b = 4 if not b: c = 6 d = 7 print("xxx: %r %r %r %r" % (a, b, c, d)) python-coverage-4.5+dfsg.1.orig/tests/farm/run/src/subdir/0000755000076600000620000000000013235414516021434 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/run/src/subdir/placeholder0000644000076600000620000000000013146037571023632 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/0000755000076600000620000000000013235414515020361 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/annotate_dir.py0000644000076600000620000000054413146037571023411 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt copy("src", "run") run(""" coverage run multi.py coverage annotate -d out_anno_dir """, rundir="run") compare("run/out_anno_dir", "gold_anno_dir", "*,cover", left_extra=True) clean("run") python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold/0000755000076600000620000000000013235414515021306 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold/white.py,cover0000644000076600000620000000126113146037571024117 0ustar staff # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # A test case sent to me by Steve White > def f(self): ! if self==1: ! pass ! elif self.m('fred'): ! pass ! elif (g==1) and (b==2): ! pass ! elif self.m('fred')==True: ! pass ! elif ((g==1) and (b==2))==True: ! pass ! else: ! pass > def g(x): > if x == 1: > a = 1 ! else: ! a = 2 > g(1) > def h(x): - if 0: #pragma: no cover - pass > if x == 1: ! a = 1 > else: > a = 2 > h(2) python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_encodings/0000755000076600000620000000000013235414515023337 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_encodings/utf8.py,cover0000644000076600000620000000036713146037571025724 0ustar staff # -*- coding: utf-8 -*- # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # This comment has an accent: é > print("spam eggs") python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_anno_dir/0000755000076600000620000000000013235414515023157 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_anno_dir/a_a.py,cover0000644000076600000620000000040313146037571025365 0ustar staff # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt > def a(x): > if x == 1: > print("x is 1") ! else: ! print("x is not 1") python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_anno_dir/a___init__.py,cover0000644000076600000620000000000013146037571026675 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_anno_dir/multi.py,cover0000644000076600000620000000032513146037571026002 0ustar staff # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt > import a.a > import b.b > a.a.a(1) > b.b.b(2) python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_anno_dir/b_b.py,cover0000644000076600000620000000033113146037571025367 0ustar staff # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt > def b(x): > msg = "x is %s" % x > print(msg) python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_anno_dir/b___init__.py,cover0000644000076600000620000000000013146037571026676 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/run_encodings.py0000644000076600000620000000054613173515667023610 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt copy("src", "out_encodings") run(""" coverage run utf8.py coverage annotate utf8.py """, rundir="out_encodings") compare("out_encodings", "gold_encodings", "*,cover") clean("out_encodings") python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/src/0000755000076600000620000000000013235414515021150 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/src/b/0000755000076600000620000000000013235414515021371 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/src/b/b.py0000644000076600000620000000031513146037571022167 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt def b(x): msg = "x is %s" % x print(msg) python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/src/b/__init__.py0000644000076600000620000000000013146037571023474 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/src/white.py0000644000076600000620000000115113146037571022644 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # A test case sent to me by Steve White def f(self): if self==1: pass elif self.m('fred'): pass elif (g==1) and (b==2): pass elif self.m('fred')==True: pass elif ((g==1) and (b==2))==True: pass else: pass def g(x): if x == 1: a = 1 else: a = 2 g(1) def h(x): if 0: #pragma: no cover pass if x == 1: a = 1 else: a = 2 h(2) python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/src/a/0000755000076600000620000000000013235414515021370 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/src/a/a.py0000644000076600000620000000036313146037571022170 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt def a(x): if x == 1: print("x is 1") else: print("x is not 1") python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/src/a/__init__.py0000644000076600000620000000000013146037571023473 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/src/utf8.py0000644000076600000620000000035113146037571022413 0ustar staff# -*- coding: utf-8 -*- # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # This comment has an accent: é print("spam eggs") python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/src/multi.py0000644000076600000620000000030513146037571022656 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt import a.a import b.b a.a.a(1) b.b.b(2) python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_multi/0000755000076600000620000000000013235414515022520 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_multi/b/0000755000076600000620000000000013235414515022741 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_multi/b/__init__.py,cover0000644000076600000620000000000013146037571026157 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_multi/b/b.py,cover0000644000076600000620000000031013146037571024645 0ustar staff # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt > def b(x): > print "x is %s" % x python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_multi/a/0000755000076600000620000000000013235414515022740 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_multi/a/__init__.py,cover0000644000076600000620000000000013146037571026156 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_multi/a/a.py,cover0000644000076600000620000000040113146037571024644 0ustar staff # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt > def a(x): > if x == 1: > print "x is 1" ! else: ! print "x is not 1" python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/gold_multi/multi.py,cover0000644000076600000620000000032513146037571025343 0ustar staff # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt > import a.a > import b.b > a.a.a(1) > b.b.b(2) python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/run_multi.py0000644000076600000620000000051313146037571022754 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt copy("src", "out_multi") run(""" coverage run multi.py coverage annotate """, rundir="out_multi") compare("out_multi", "gold_multi", "*,cover") clean("out_multi") python-coverage-4.5+dfsg.1.orig/tests/farm/annotate/run.py0000644000076600000620000000046613146037571021551 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt copy("src", "out") run(""" coverage run white.py coverage annotate white.py """, rundir="out") compare("out", "gold", "*,cover") clean("out") python-coverage-4.5+dfsg.1.orig/tests/test_phystokens.py0000644000076600000620000002417113173515667021463 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for coverage.py's improved tokenizer.""" import os.path import re import textwrap from coverage import env from coverage.phystokens import source_token_lines, source_encoding from coverage.phystokens import neuter_encoding_declaration, compile_unicode from coverage.python import get_python_source from tests.coveragetest import CoverageTest, TESTS_DIR # A simple program and its token stream. SIMPLE = u"""\ # yay! def foo(): say('two = %d' % 2) """ SIMPLE_TOKENS = [ [('com', "# yay!")], [('key', 'def'), ('ws', ' '), ('nam', 'foo'), ('op', '('), ('op', ')'), ('op', ':')], [('ws', ' '), ('nam', 'say'), ('op', '('), ('str', "'two = %d'"), ('ws', ' '), ('op', '%'), ('ws', ' '), ('num', '2'), ('op', ')')], ] # Mixed-whitespace program, and its token stream. MIXED_WS = u"""\ def hello(): a="Hello world!" \tb="indented" """ MIXED_WS_TOKENS = [ [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ('op', ')'), ('op', ':')], [('ws', ' '), ('nam', 'a'), ('op', '='), ('str', '"Hello world!"')], [('ws', ' '), ('nam', 'b'), ('op', '='), ('str', '"indented"')], ] class PhysTokensTest(CoverageTest): """Tests for coverage.py's improved tokenizer.""" run_in_temp_dir = False def check_tokenization(self, source): """Tokenize `source`, then put it back together, should be the same.""" tokenized = "" for line in source_token_lines(source): text = "".join(t for _, t in line) tokenized += text + "\n" # source_token_lines doesn't preserve trailing spaces, so trim all that # before comparing. source = source.replace('\r\n', '\n') source = re.sub(r"(?m)[ \t]+$", "", source) tokenized = re.sub(r"(?m)[ \t]+$", "", tokenized) self.assertMultiLineEqual(source, tokenized) def check_file_tokenization(self, fname): """Use the contents of `fname` for `check_tokenization`.""" self.check_tokenization(get_python_source(fname)) def test_simple(self): self.assertEqual(list(source_token_lines(SIMPLE)), SIMPLE_TOKENS) self.check_tokenization(SIMPLE) def test_missing_final_newline(self): # We can tokenize source that is missing the final newline. self.assertEqual(list(source_token_lines(SIMPLE.rstrip())), SIMPLE_TOKENS) def test_tab_indentation(self): # Mixed tabs and spaces... self.assertEqual(list(source_token_lines(MIXED_WS)), MIXED_WS_TOKENS) def test_tokenize_real_file(self): # Check the tokenization of a real file (large, btw). real_file = os.path.join(TESTS_DIR, "test_coverage.py") self.check_file_tokenization(real_file) def test_stress(self): # Check the tokenization of a stress-test file. stress = os.path.join(TESTS_DIR, "stress_phystoken.tok") self.check_file_tokenization(stress) stress = os.path.join(TESTS_DIR, "stress_phystoken_dos.tok") self.check_file_tokenization(stress) # The default encoding is different in Python 2 and Python 3. if env.PY3: DEF_ENCODING = "utf-8" else: DEF_ENCODING = "ascii" ENCODING_DECLARATION_SOURCES = [ # Various forms from http://www.python.org/dev/peps/pep-0263/ (1, b"# coding=cp850\n\n", "cp850"), (1, b"# coding=latin-1\n", "iso-8859-1"), (1, b"# coding=iso-latin-1\n", "iso-8859-1"), (1, b"#!/usr/bin/python\n# -*- coding: cp850 -*-\n", "cp850"), (1, b"#!/usr/bin/python\n# vim: set fileencoding=cp850:\n", "cp850"), (1, b"# This Python file uses this encoding: cp850\n", "cp850"), (1, b"# This file uses a different encoding:\n# coding: cp850\n", "cp850"), (1, b"\n# coding=cp850\n\n", "cp850"), (2, b"# -*- coding:cp850 -*-\n# vim: fileencoding=cp850\n", "cp850"), ] class SourceEncodingTest(CoverageTest): """Tests of source_encoding() for detecting encodings.""" run_in_temp_dir = False def test_detect_source_encoding(self): for _, source, expected in ENCODING_DECLARATION_SOURCES: self.assertEqual( source_encoding(source), expected, "Wrong encoding in %r" % source ) def test_detect_source_encoding_not_in_comment(self): if env.PYPY and env.PY3: # pragma: no metacov # PyPy3 gets this case wrong. Not sure what I can do about it, # so skip the test. self.skipTest("PyPy3 is wrong about non-comment encoding. Skip it.") # Should not detect anything here source = b'def parse(src, encoding=None):\n pass' self.assertEqual(source_encoding(source), DEF_ENCODING) def test_dont_detect_source_encoding_on_third_line(self): # A coding declaration doesn't count on the third line. source = b"\n\n# coding=cp850\n\n" self.assertEqual(source_encoding(source), DEF_ENCODING) def test_detect_source_encoding_of_empty_file(self): # An important edge case. self.assertEqual(source_encoding(b""), DEF_ENCODING) def test_bom(self): # A BOM means utf-8. source = b"\xEF\xBB\xBFtext = 'hello'\n" self.assertEqual(source_encoding(source), 'utf-8-sig') def test_bom_with_encoding(self): source = b"\xEF\xBB\xBF# coding: utf-8\ntext = 'hello'\n" self.assertEqual(source_encoding(source), 'utf-8-sig') def test_bom_is_wrong(self): # A BOM with an explicit non-utf8 encoding is an error. source = b"\xEF\xBB\xBF# coding: cp850\n" with self.assertRaisesRegex(SyntaxError, "encoding problem: utf-8"): source_encoding(source) def test_unknown_encoding(self): source = b"# coding: klingon\n" with self.assertRaisesRegex(SyntaxError, "unknown encoding: klingon"): source_encoding(source) class NeuterEncodingDeclarationTest(CoverageTest): """Tests of phystokens.neuter_encoding_declaration().""" run_in_temp_dir = False def test_neuter_encoding_declaration(self): for lines_diff_expected, source, _ in ENCODING_DECLARATION_SOURCES: neutered = neuter_encoding_declaration(source.decode("ascii")) neutered = neutered.encode("ascii") # The neutered source should have the same number of lines. source_lines = source.splitlines() neutered_lines = neutered.splitlines() self.assertEqual(len(source_lines), len(neutered_lines)) # Only one of the lines should be different. lines_different = sum( int(nline != sline) for nline, sline in zip(neutered_lines, source_lines) ) self.assertEqual(lines_diff_expected, lines_different) # The neutered source will be detected as having no encoding # declaration. self.assertEqual( source_encoding(neutered), DEF_ENCODING, "Wrong encoding in %r" % neutered ) def test_two_encoding_declarations(self): input_src = textwrap.dedent(u"""\ # -*- coding: ascii -*- # -*- coding: utf-8 -*- # -*- coding: utf-16 -*- """) expected_src = textwrap.dedent(u"""\ # (deleted declaration) -*- # (deleted declaration) -*- # -*- coding: utf-16 -*- """) output_src = neuter_encoding_declaration(input_src) self.assertEqual(expected_src, output_src) def test_one_encoding_declaration(self): input_src = textwrap.dedent(u"""\ # -*- coding: utf-16 -*- # Just a comment. # -*- coding: ascii -*- """) expected_src = textwrap.dedent(u"""\ # (deleted declaration) -*- # Just a comment. # -*- coding: ascii -*- """) output_src = neuter_encoding_declaration(input_src) self.assertEqual(expected_src, output_src) class Bug529Test(CoverageTest): """Test of bug 529""" def test_bug_529(self): # Don't over-neuter coding declarations. This happened with a test # file which contained code in multi-line strings, all with coding # declarations. The neutering of the file also changed the multi-line # strings, which it shouldn't have. self.make_file("the_test.py", '''\ # -*- coding: utf-8 -*- import unittest class Bug529Test(unittest.TestCase): def test_two_strings_are_equal(self): src1 = u"""\\ # -*- coding: utf-8 -*- # Just a comment. """ src2 = u"""\\ # -*- coding: utf-8 -*- # Just a comment. """ self.assertEqual(src1, src2) if __name__ == "__main__": unittest.main() ''') status, out = self.run_command_status("coverage run the_test.py") self.assertEqual(status, 0) self.assertIn("OK", out) # If this test fails, the output will be super-confusing, because it # has a failing unit test contained within the failing unit test. class CompileUnicodeTest(CoverageTest): """Tests of compiling Unicode strings.""" run_in_temp_dir = False def assert_compile_unicode(self, source): """Assert that `source` will compile properly with `compile_unicode`.""" source += u"a = 42\n" # This doesn't raise an exception: code = compile_unicode(source, "", "exec") globs = {} exec(code, globs) self.assertEqual(globs['a'], 42) def test_cp1252(self): uni = u"""# coding: cp1252\n# \u201C curly \u201D\n""" self.assert_compile_unicode(uni) def test_double_coding_declaration(self): # Build this string in a weird way so that actual vim's won't try to # interpret it... uni = u"# -*- coding:utf-8 -*-\n# v" "im: fileencoding=utf-8\n" self.assert_compile_unicode(uni) python-coverage-4.5+dfsg.1.orig/tests/test_coverage.py0000644000076600000620000013654013177624110021037 0ustar staff# coding: utf-8 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for coverage.py.""" import coverage from coverage import env from coverage.misc import CoverageException from tests.coveragetest import CoverageTest class TestCoverageTest(CoverageTest): """Make sure our complex self.check_coverage method works.""" def test_successful_coverage(self): # The simplest run possible. self.check_coverage("""\ a = 1 b = 2 """, [1,2] ) # You can provide a list of possible statement matches. self.check_coverage("""\ a = 1 b = 2 """, ([100], [1,2], [1723,47]), ) # You can specify missing lines. self.check_coverage("""\ a = 1 if a == 2: a = 3 """, [1,2,3], missing="3", ) # You can specify a list of possible missing lines. self.check_coverage("""\ a = 1 if a == 2: a = 3 """, [1,2,3], missing=("47-49", "3", "100,102") ) def test_failed_coverage(self): # If the lines are wrong, the message shows right and wrong. with self.assertRaisesRegex(AssertionError, r"\[1, 2] != \[1]"): self.check_coverage("""\ a = 1 b = 2 """, [1] ) # If the list of lines possibilities is wrong, the msg shows right. msg = r"None of the lines choices matched \[1, 2]" with self.assertRaisesRegex(AssertionError, msg): self.check_coverage("""\ a = 1 b = 2 """, ([1], [2]) ) # If the missing lines are wrong, the message shows right and wrong. with self.assertRaisesRegex(AssertionError, r"'3' != '37'"): self.check_coverage("""\ a = 1 if a == 2: a = 3 """, [1,2,3], missing="37", ) # If the missing lines possibilities are wrong, the msg shows right. msg = r"None of the missing choices matched '3'" with self.assertRaisesRegex(AssertionError, msg): self.check_coverage("""\ a = 1 if a == 2: a = 3 """, [1,2,3], missing=("37", "4-10"), ) class BasicCoverageTest(CoverageTest): """The simplest tests, for quick smoke testing of fundamental changes.""" def test_simple(self): self.check_coverage("""\ a = 1 b = 2 c = 4 # Nothing here d = 6 """, [1,2,4,6], report="4 0 0 0 100%") def test_indentation_wackiness(self): # Partial final lines are OK. self.check_coverage("""\ import sys if not sys.path: a = 1 """, # indented last line [1,2,3], "3") def test_multiline_initializer(self): self.check_coverage("""\ d = { 'foo': 1+2, 'bar': (lambda x: x+1)(1), 'baz': str(1), } e = { 'foo': 1, 'bar': 2 } """, [1,7], "") def test_list_comprehension(self): self.check_coverage("""\ l = [ 2*i for i in range(10) if i > 5 ] assert l == [12, 14, 16, 18] """, [1,5], "") class SimpleStatementTest(CoverageTest): """Testing simple single-line statements.""" def test_expression(self): # Bare expressions as statements are tricky: some implementations # optimize some of them away. All implementations seem to count # the implicit return at the end as executable. self.check_coverage("""\ 12 23 """, ([1,2],[2]), "") self.check_coverage("""\ 12 23 a = 3 """, ([1,2,3],[3]), "") self.check_coverage("""\ 1 + 2 1 + \\ 2 """, ([1,2], [2]), "") self.check_coverage("""\ 1 + 2 1 + \\ 2 a = 4 """, ([1,2,4], [4]), "") def test_assert(self): self.check_coverage("""\ assert (1 + 2) assert (1 + 2) assert (1 + 2), 'the universe is broken' assert (1 + 2), \\ 'something is amiss' """, [1,2,4,5], "") def test_assignment(self): # Simple variable assignment self.check_coverage("""\ a = (1 + 2) b = (1 + 2) c = \\ 1 """, [1,2,4], "") def test_assign_tuple(self): self.check_coverage("""\ a = 1 a,b,c = 7,8,9 assert a == 7 and b == 8 and c == 9 """, [1,2,3], "") def test_more_assignments(self): self.check_coverage("""\ x = [] d = {} d[ 4 + len(x) + 5 ] = \\ d[ 8 ** 2 ] = \\ 9 """, [1, 2, 3], "") def test_attribute_assignment(self): # Attribute assignment self.check_coverage("""\ class obj: pass o = obj() o.foo = (1 + 2) o.foo = (1 + 2) o.foo = \\ 1 """, [1,2,3,4,6], "") def test_list_of_attribute_assignment(self): self.check_coverage("""\ class obj: pass o = obj() o.a, o.b = (1 + 2), 3 o.a, o.b = (1 + 2), (3 + 4) o.a, o.b = \\ 1, \\ 2 """, [1,2,3,4,7], "") def test_augmented_assignment(self): self.check_coverage("""\ a = 1 a += 1 a += (1 + 2) a += \\ 1 """, [1,2,3,5], "") def test_triple_string_stuff(self): self.check_coverage("""\ a = ''' a multiline string. ''' b = ''' long expression ''' + ''' on many lines. ''' c = len(''' long expression ''' + ''' on many lines. ''') """, [1,5,11], "") def test_pass(self): # pass is tricky: if it's the only statement in a block, then it is # "executed". But if it is not the only statement, then it is not. self.check_coverage("""\ if 1==1: pass """, [1,2], "") self.check_coverage("""\ def foo(): pass foo() """, [1,2,3], "") self.check_coverage("""\ def foo(): "doc" pass foo() """, ([1,3,4], [1,4]), "") self.check_coverage("""\ class Foo: def foo(self): pass Foo().foo() """, [1,2,3,4], "") self.check_coverage("""\ class Foo: def foo(self): "Huh?" pass Foo().foo() """, ([1,2,4,5], [1,2,5]), "") def test_del(self): self.check_coverage("""\ d = { 'a': 1, 'b': 1, 'c': 1, 'd': 1, 'e': 1 } del d['a'] del d[ 'b' ] del d['c'], \\ d['d'], \\ d['e'] assert(len(d.keys()) == 0) """, [1,2,3,6,9], "") def test_print(self): if env.PY3: # Print statement is gone in Py3k. self.skipTest("No more print statement in Python 3.") self.check_coverage("""\ print "hello, world!" print ("hey: %d" % 17) print "goodbye" print "hello, world!", print ("hey: %d" % 17), print "goodbye", """, [1,2,4,5,6,8], "") def test_raise(self): self.check_coverage("""\ try: raise Exception( "hello %d" % 17) except: pass """, [1,2,5,6], "") def test_return(self): self.check_coverage("""\ def fn(): a = 1 return a x = fn() assert(x == 1) """, [1,2,3,5,6], "") self.check_coverage("""\ def fn(): a = 1 return ( a + 1) x = fn() assert(x == 2) """, [1,2,3,7,8], "") self.check_coverage("""\ def fn(): a = 1 return (a, a + 1, a + 2) x,y,z = fn() assert x == 1 and y == 2 and z == 3 """, [1,2,3,7,8], "") def test_yield(self): self.check_coverage("""\ def gen(): yield 1 yield (2+ 3+ 4) yield 1, \\ 2 a,b,c = gen() assert a == 1 and b == 9 and c == (1,2) """, [1,2,3,6,8,9], "") def test_break(self): self.check_coverage("""\ for x in range(10): a = 2 + x break a = 4 assert a == 2 """, [1,2,3,4,5], "4") def test_continue(self): self.check_coverage("""\ for x in range(10): a = 2 + x continue a = 4 assert a == 11 """, [1,2,3,4,5], "4") def test_strange_unexecuted_continue(self): # pragma: not covered # Peephole optimization of jumps to jumps can mean that some statements # never hit the line tracer. The behavior is different in different # versions of Python, so don't run this test: self.skipTest("Expected failure: peephole optimization of jumps to jumps") self.check_coverage("""\ a = b = c = 0 for n in range(100): if n % 2: if n % 4: a += 1 continue # <-- This line may not be hit. else: b += 1 c += 1 assert a == 50 and b == 50 and c == 50 a = b = c = 0 for n in range(100): if n % 2: if n % 3: a += 1 continue # <-- This line is always hit. else: b += 1 c += 1 assert a == 33 and b == 50 and c == 50 """, [1,2,3,4,5,6,8,9,10, 12,13,14,15,16,17,19,20,21], "") def test_import(self): self.check_coverage("""\ import string from sys import path a = 1 """, [1,2,3], "") self.check_coverage("""\ import string if 1 == 2: from sys import path a = 1 """, [1,2,3,4], "3") self.check_coverage("""\ import string, \\ os, \\ re from sys import path, \\ stdout a = 1 """, [1,4,6], "") self.check_coverage("""\ import sys, sys as s assert s.path == sys.path """, [1,2], "") self.check_coverage("""\ import sys, \\ sys as s assert s.path == sys.path """, [1,3], "") self.check_coverage("""\ from sys import path, \\ path as p assert p == path """, [1,3], "") self.check_coverage("""\ from sys import \\ * assert len(path) > 0 """, [1,3], "") def test_global(self): self.check_coverage("""\ g = h = i = 1 def fn(): global g global h, \\ i g = h = i = 2 fn() assert g == 2 and h == 2 and i == 2 """, [1,2,6,7,8], "") self.check_coverage("""\ g = h = i = 1 def fn(): global g; g = 2 fn() assert g == 2 and h == 1 and i == 1 """, [1,2,3,4,5], "") def test_exec(self): self.check_coverage("""\ a = b = c = 1 exec("a = 2") exec("b = " + "c = " + "2") assert a == 2 and b == 2 and c == 2 """, [1,2,3,6], "") self.check_coverage("""\ vars = {'a': 1, 'b': 1, 'c': 1} exec("a = 2", vars) exec("b = " + "c = " + "2", vars) assert vars['a'] == 2 and vars['b'] == 2 and vars['c'] == 2 """, [1,2,3,6], "") self.check_coverage("""\ globs = {} locs = {'a': 1, 'b': 1, 'c': 1} exec("a = 2", globs, locs) exec("b = " + "c = " + "2", globs, locs) assert locs['a'] == 2 and locs['b'] == 2 and locs['c'] == 2 """, [1,2,3,4,7], "") def test_extra_doc_string(self): self.check_coverage("""\ a = 1 "An extra docstring, should be a comment." b = 3 assert (a,b) == (1,3) """, [1,3,4], "") self.check_coverage("""\ a = 1 "An extra docstring, should be a comment." b = 3 123 # A number for some reason: ignored 1+1 # An expression: executed. c = 6 assert (a,b,c) == (1,3,6) """, ([1,3,6,7], [1,3,5,6,7], [1,3,4,5,6,7]), "") def test_nonascii(self): self.check_coverage("""\ # coding: utf-8 a = 2 b = 3 """, [2, 3] ) def test_module_docstring(self): self.check_coverage("""\ '''I am a module docstring.''' a = 2 b = 3 """, [2, 3] ) if env.PYVERSION < (3, 7): # Before 3.7, module docstrings were included in the lnotab table, # unless they were the first line in the file? lines = [2, 3, 4] else: lines = [3, 4] self.check_coverage("""\ # Start with a comment, because it changes the behavior(!?) '''I am a module docstring.''' a = 3 b = 4 """, lines ) class CompoundStatementTest(CoverageTest): """Testing coverage of multi-line compound statements.""" def test_statement_list(self): self.check_coverage("""\ a = 1; b = 2; c = 3 d = 4; e = 5; assert (a,b,c,d,e) == (1,2,3,4,5) """, [1,2,3,5], "") def test_if(self): self.check_coverage("""\ a = 1 if a == 1: x = 3 assert x == 3 if (a == 1): x = 7 assert x == 7 """, [1,2,3,4,5,7,8], "") self.check_coverage("""\ a = 1 if a == 1: x = 3 else: y = 5 assert x == 3 """, [1,2,3,5,6], "5") self.check_coverage("""\ a = 1 if a != 1: x = 3 else: y = 5 assert y == 5 """, [1,2,3,5,6], "3") self.check_coverage("""\ a = 1; b = 2 if a == 1: if b == 2: x = 4 else: y = 6 else: z = 8 assert x == 4 """, [1,2,3,4,6,8,9], "6-8") def test_elif(self): self.check_coverage("""\ a = 1; b = 2; c = 3; if a == 1: x = 3 elif b == 2: y = 5 else: z = 7 assert x == 3 """, [1,2,3,4,5,7,8], "4-7", report="7 3 4 1 45% 4-7, 2->4", ) self.check_coverage("""\ a = 1; b = 2; c = 3; if a != 1: x = 3 elif b == 2: y = 5 else: z = 7 assert y == 5 """, [1,2,3,4,5,7,8], "3, 7", report="7 2 4 2 64% 3, 7, 2->3, 4->7", ) self.check_coverage("""\ a = 1; b = 2; c = 3; if a != 1: x = 3 elif b != 2: y = 5 else: z = 7 assert z == 7 """, [1,2,3,4,5,7,8], "3, 5", report="7 2 4 2 64% 3, 5, 2->3, 4->5", ) def test_elif_no_else(self): self.check_coverage("""\ a = 1; b = 2; c = 3; if a == 1: x = 3 elif b == 2: y = 5 assert x == 3 """, [1,2,3,4,5,6], "4-5", report="6 2 4 1 50% 4-5, 2->4", ) self.check_coverage("""\ a = 1; b = 2; c = 3; if a != 1: x = 3 elif b == 2: y = 5 assert y == 5 """, [1,2,3,4,5,6], "3", report="6 1 4 2 70% 3, 2->3, 4->6", ) def test_elif_bizarre(self): self.check_coverage("""\ def f(self): if self==1: x = 3 elif self.m('fred'): x = 5 elif (g==1) and (b==2): x = 7 elif self.m('fred')==True: x = 9 elif ((g==1) and (b==2))==True: x = 11 else: x = 13 """, [1,2,3,4,5,6,7,8,9,10,11,13], "2-13") def test_split_if(self): self.check_coverage("""\ a = 1; b = 2; c = 3; if \\ a == 1: x = 3 elif \\ b == 2: y = 5 else: z = 7 assert x == 3 """, [1,2,4,5,7,9,10], "5-9") self.check_coverage("""\ a = 1; b = 2; c = 3; if \\ a != 1: x = 3 elif \\ b == 2: y = 5 else: z = 7 assert y == 5 """, [1,2,4,5,7,9,10], "4, 9") self.check_coverage("""\ a = 1; b = 2; c = 3; if \\ a != 1: x = 3 elif \\ b != 2: y = 5 else: z = 7 assert z == 7 """, [1,2,4,5,7,9,10], "4, 7") def test_pathological_split_if(self): self.check_coverage("""\ a = 1; b = 2; c = 3; if ( a == 1 ): x = 3 elif ( b == 2 ): y = 5 else: z = 7 assert x == 3 """, [1,2,5,6,9,11,12], "6-11") self.check_coverage("""\ a = 1; b = 2; c = 3; if ( a != 1 ): x = 3 elif ( b == 2 ): y = 5 else: z = 7 assert y == 5 """, [1,2,5,6,9,11,12], "5, 11") self.check_coverage("""\ a = 1; b = 2; c = 3; if ( a != 1 ): x = 3 elif ( b != 2 ): y = 5 else: z = 7 assert z == 7 """, [1,2,5,6,9,11,12], "5, 9") def test_absurd_split_if(self): self.check_coverage("""\ a = 1; b = 2; c = 3; if a == 1 \\ : x = 3 elif b == 2 \\ : y = 5 else: z = 7 assert x == 3 """, [1,2,4,5,7,9,10], "5-9") self.check_coverage("""\ a = 1; b = 2; c = 3; if a != 1 \\ : x = 3 elif b == 2 \\ : y = 5 else: z = 7 assert y == 5 """, [1,2,4,5,7,9,10], "4, 9") self.check_coverage("""\ a = 1; b = 2; c = 3; if a != 1 \\ : x = 3 elif b != 2 \\ : y = 5 else: z = 7 assert z == 7 """, [1,2,4,5,7,9,10], "4, 7") def test_constant_if(self): self.check_coverage("""\ if 1: a = 2 assert a == 2 """, [2,3], "") def test_while(self): self.check_coverage("""\ a = 3; b = 0 while a: b += 1 a -= 1 assert a == 0 and b == 3 """, [1,2,3,4,5], "") self.check_coverage("""\ a = 3; b = 0 while a: b += 1 break b = 99 assert a == 3 and b == 1 """, [1,2,3,4,5,6], "5") def test_while_else(self): # Take the else branch. self.check_coverage("""\ a = 3; b = 0 while a: b += 1 a -= 1 else: b = 99 assert a == 0 and b == 99 """, [1,2,3,4,6,7], "") # Don't take the else branch. self.check_coverage("""\ a = 3; b = 0 while a: b += 1 a -= 1 break b = 123 else: b = 99 assert a == 2 and b == 1 """, [1,2,3,4,5,6,8,9], "6-8") def test_split_while(self): self.check_coverage("""\ a = 3; b = 0 while \\ a: b += 1 a -= 1 assert a == 0 and b == 3 """, [1,2,4,5,6], "") self.check_coverage("""\ a = 3; b = 0 while ( a ): b += 1 a -= 1 assert a == 0 and b == 3 """, [1,2,5,6,7], "") def test_for(self): self.check_coverage("""\ a = 0 for i in [1,2,3,4,5]: a += i assert a == 15 """, [1,2,3,4], "") self.check_coverage("""\ a = 0 for i in [1, 2,3,4, 5]: a += i assert a == 15 """, [1,2,5,6], "") self.check_coverage("""\ a = 0 for i in [1,2,3,4,5]: a += i break a = 99 assert a == 1 """, [1,2,3,4,5,6], "5") def test_for_else(self): self.check_coverage("""\ a = 0 for i in range(5): a += i+1 else: a = 99 assert a == 99 """, [1,2,3,5,6], "") self.check_coverage("""\ a = 0 for i in range(5): a += i+1 break a = 99 else: a = 123 assert a == 1 """, [1,2,3,4,5,7,8], "5-7") def test_split_for(self): self.check_coverage("""\ a = 0 for \\ i in [1,2,3,4,5]: a += i assert a == 15 """, [1,2,4,5], "") self.check_coverage("""\ a = 0 for \\ i in [1, 2,3,4, 5]: a += i assert a == 15 """, [1,2,6,7], "") def test_try_except(self): self.check_coverage("""\ a = 0 try: a = 1 except: a = 99 assert a == 1 """, [1,2,3,4,5,6], "4-5") self.check_coverage("""\ a = 0 try: a = 1 raise Exception("foo") except: a = 99 assert a == 99 """, [1,2,3,4,5,6,7], "") self.check_coverage("""\ a = 0 try: a = 1 raise Exception("foo") except ImportError: a = 99 except: a = 123 assert a == 123 """, [1,2,3,4,5,6,7,8,9], "6") self.check_coverage("""\ a = 0 try: a = 1 raise IOError("foo") except ImportError: a = 99 except IOError: a = 17 except: a = 123 assert a == 17 """, [1,2,3,4,5,6,7,8,9,10,11], "6, 9-10") self.check_coverage("""\ a = 0 try: a = 1 except: a = 99 else: a = 123 assert a == 123 """, [1,2,3,4,5,7,8], "4-5", arcz=".1 12 23 45 58 37 78 8.", arcz_missing="45 58", ) self.check_coverage("""\ a = 0 try: a = 1 raise Exception("foo") except: a = 99 else: a = 123 assert a == 99 """, [1,2,3,4,5,6,8,9], "8", arcz=".1 12 23 34 45 56 69 89 9.", arcz_missing="89", ) def test_try_finally(self): self.check_coverage("""\ a = 0 try: a = 1 finally: a = 99 assert a == 99 """, [1,2,3,5,6], "") self.check_coverage("""\ a = 0; b = 0 try: a = 1 try: raise Exception("foo") finally: b = 123 except: a = 99 assert a == 99 and b == 123 """, [1,2,3,4,5,7,8,9,10], "") def test_function_def(self): self.check_coverage("""\ a = 99 def foo(): ''' docstring ''' return 1 a = foo() assert a == 1 """, [1,2,5,7,8], "") self.check_coverage("""\ def foo( a, b ): ''' docstring ''' return a+b x = foo(17, 23) assert x == 40 """, [1,7,9,10], "") self.check_coverage("""\ def foo( a = (lambda x: x*2)(10), b = ( lambda x: x+1 )(1) ): ''' docstring ''' return a+b x = foo() assert x == 22 """, [1,10,12,13], "") def test_class_def(self): if env.PYVERSION < (3, 7): arcz="-22 2D DE E-2 23 36 6A A-2 -68 8-6 -AB B-A" else: # Python 3.7 no longer includes class docstrings in the lnotab table. arcz="-22 2D DE E-2 26 6A A-2 -68 8-6 -AB B-A" self.check_coverage("""\ # A comment. class theClass: ''' the docstring. Don't be fooled. ''' def __init__(self): ''' Another docstring. ''' self.a = 1 def foo(self): return self.a x = theClass().foo() assert x == 1 """, [2, 6, 8, 10, 11, 13, 14], "", arcz=arcz, ) class ExcludeTest(CoverageTest): """Tests of the exclusion feature to mark lines as not covered.""" def test_default(self): # A number of forms of pragma comment are accepted. self.check_coverage("""\ a = 1 b = 2 # pragma: no cover c = 3 d = 4 #pragma NOCOVER e = 5 f = 6#\tpragma:\tno cover g = 7 """, [1,3,5,7] ) def test_simple(self): self.check_coverage("""\ a = 1; b = 2 if len([]): a = 4 # -cc """, [1,3], "", excludes=['-cc']) def test_two_excludes(self): self.check_coverage("""\ a = 1; b = 2 if a == 99: a = 4 # -cc b = 5 c = 6 # -xx assert a == 1 and b == 2 """, [1,3,5,7], "5", excludes=['-cc', '-xx']) def test_excluding_if_suite(self): self.check_coverage("""\ a = 1; b = 2 if len([]): # not-here a = 4 b = 5 c = 6 assert a == 1 and b == 2 """, [1,7], "", excludes=['not-here']) def test_excluding_if_but_not_else_suite(self): self.check_coverage("""\ a = 1; b = 2 if len([]): # not-here a = 4 b = 5 c = 6 else: a = 8 b = 9 assert a == 8 and b == 9 """, [1,8,9,10], "", excludes=['not-here']) def test_excluding_else_suite(self): self.check_coverage("""\ a = 1; b = 2 if 1==1: a = 4 b = 5 c = 6 else: #pragma: NO COVER a = 8 b = 9 assert a == 4 and b == 5 and c == 6 """, [1,3,4,5,6,10], "", excludes=['#pragma: NO COVER']) self.check_coverage("""\ a = 1; b = 2 if 1==1: a = 4 b = 5 c = 6 # Lots of comments to confuse the else handler. # more. else: #pragma: NO COVER # Comments here too. a = 8 b = 9 assert a == 4 and b == 5 and c == 6 """, [1,3,4,5,6,17], "", excludes=['#pragma: NO COVER']) def test_excluding_elif_suites(self): self.check_coverage("""\ a = 1; b = 2 if 1==1: a = 4 b = 5 c = 6 elif 1==0: #pragma: NO COVER a = 8 b = 9 else: a = 11 b = 12 assert a == 4 and b == 5 and c == 6 """, [1,3,4,5,6,11,12,13], "11-12", excludes=['#pragma: NO COVER']) def test_excluding_oneline_if(self): self.check_coverage("""\ def foo(): a = 2 if len([]): x = 3 # no cover b = 4 foo() """, [1,2,4,6], "", excludes=["no cover"]) def test_excluding_a_colon_not_a_suite(self): self.check_coverage("""\ def foo(): l = list(range(10)) a = l[:3] # no cover b = 4 foo() """, [1,2,4,6], "", excludes=["no cover"]) def test_excluding_for_suite(self): self.check_coverage("""\ a = 0 for i in [1,2,3,4,5]: #pragma: NO COVER a += i assert a == 15 """, [1,4], "", excludes=['#pragma: NO COVER']) self.check_coverage("""\ a = 0 for i in [1, 2,3,4, 5]: #pragma: NO COVER a += i assert a == 15 """, [1,6], "", excludes=['#pragma: NO COVER']) self.check_coverage("""\ a = 0 for i in [1,2,3,4,5 ]: #pragma: NO COVER a += i break a = 99 assert a == 1 """, [1,7], "", excludes=['#pragma: NO COVER']) def test_excluding_for_else(self): self.check_coverage("""\ a = 0 for i in range(5): a += i+1 break a = 99 else: #pragma: NO COVER a = 123 assert a == 1 """, [1,2,3,4,5,8], "5", excludes=['#pragma: NO COVER']) def test_excluding_while(self): self.check_coverage("""\ a = 3; b = 0 while a*b: #pragma: NO COVER b += 1 break b = 99 assert a == 3 and b == 0 """, [1,6], "", excludes=['#pragma: NO COVER']) self.check_coverage("""\ a = 3; b = 0 while ( a*b ): #pragma: NO COVER b += 1 break b = 99 assert a == 3 and b == 0 """, [1,8], "", excludes=['#pragma: NO COVER']) def test_excluding_while_else(self): self.check_coverage("""\ a = 3; b = 0 while a: b += 1 break b = 99 else: #pragma: NO COVER b = 123 assert a == 3 and b == 1 """, [1,2,3,4,5,8], "5", excludes=['#pragma: NO COVER']) def test_excluding_try_except(self): self.check_coverage("""\ a = 0 try: a = 1 except: #pragma: NO COVER a = 99 assert a == 1 """, [1,2,3,6], "", excludes=['#pragma: NO COVER']) self.check_coverage("""\ a = 0 try: a = 1 raise Exception("foo") except: a = 99 assert a == 99 """, [1,2,3,4,5,6,7], "", excludes=['#pragma: NO COVER']) self.check_coverage("""\ a = 0 try: a = 1 raise Exception("foo") except ImportError: #pragma: NO COVER a = 99 except: a = 123 assert a == 123 """, [1,2,3,4,7,8,9], "", excludes=['#pragma: NO COVER']) self.check_coverage("""\ a = 0 try: a = 1 except: #pragma: NO COVER a = 99 else: a = 123 assert a == 123 """, [1,2,3,7,8], "", excludes=['#pragma: NO COVER'], arcz=".1 12 23 37 45 58 78 8.", arcz_missing="45 58", ) self.check_coverage("""\ a = 0 try: a = 1 raise Exception("foo") except: a = 99 else: #pragma: NO COVER a = 123 assert a == 99 """, [1,2,3,4,5,6,9], "", excludes=['#pragma: NO COVER'], arcz=".1 12 23 34 45 56 69 89 9.", arcz_missing="89", ) def test_excluding_try_except_pass(self): self.check_coverage("""\ a = 0 try: a = 1 except: #pragma: NO COVER x = 2 assert a == 1 """, [1,2,3,6], "", excludes=['#pragma: NO COVER']) self.check_coverage("""\ a = 0 try: a = 1 raise Exception("foo") except ImportError: #pragma: NO COVER x = 2 except: a = 123 assert a == 123 """, [1,2,3,4,7,8,9], "", excludes=['#pragma: NO COVER']) self.check_coverage("""\ a = 0 try: a = 1 except: #pragma: NO COVER x = 2 else: a = 123 assert a == 123 """, [1,2,3,7,8], "", excludes=['#pragma: NO COVER'], arcz=".1 12 23 37 45 58 78 8.", arcz_missing="45 58", ) self.check_coverage("""\ a = 0 try: a = 1 raise Exception("foo") except: a = 99 else: #pragma: NO COVER x = 2 assert a == 99 """, [1,2,3,4,5,6,9], "", excludes=['#pragma: NO COVER'], arcz=".1 12 23 34 45 56 69 89 9.", arcz_missing="89", ) def test_excluding_if_pass(self): # From a comment on the coverage.py page by Michael McNeil Forbes: self.check_coverage("""\ def f(): if False: # pragma: no cover pass # This line still reported as missing if False: # pragma: no cover x = 1 # Now it is skipped. f() """, [1,7], "", excludes=["no cover"]) def test_excluding_function(self): self.check_coverage("""\ def fn(foo): #pragma: NO COVER a = 1 b = 2 c = 3 x = 1 assert x == 1 """, [6,7], "", excludes=['#pragma: NO COVER']) def test_excluding_method(self): self.check_coverage("""\ class Fooey: def __init__(self): self.a = 1 def foo(self): #pragma: NO COVER return self.a x = Fooey() assert x.a == 1 """, [1,2,3,8,9], "", excludes=['#pragma: NO COVER']) def test_excluding_class(self): self.check_coverage("""\ class Fooey: #pragma: NO COVER def __init__(self): self.a = 1 def foo(self): return self.a x = 1 assert x == 1 """, [8,9], "", excludes=['#pragma: NO COVER']) def test_excludes_non_ascii(self): self.check_coverage("""\ # coding: utf-8 a = 1; b = 2 if len([]): a = 5 # ✘cover """, [2, 4], "", excludes=['✘cover'] ) def test_formfeed(self): # https://bitbucket.org/ned/coveragepy/issues/461/multiline-asserts-need-too-many-pragma self.check_coverage("""\ x = 1 assert len([]) == 0, ( "This won't happen %s" % ("hello",) ) \f x = 6 assert len([]) == 0, ( "This won't happen %s" % ("hello",) ) """, [1, 6], "", excludes=['assert'], ) class Py24Test(CoverageTest): """Tests of new syntax in Python 2.4.""" def test_function_decorators(self): self.check_coverage("""\ def require_int(func): def wrapper(arg): assert isinstance(arg, int) return func(arg) return wrapper @require_int def p1(arg): return arg*2 assert p1(10) == 20 """, [1,2,3,4,6,8,10,12], "") def test_function_decorators_with_args(self): self.check_coverage("""\ def boost_by(extra): def decorator(func): def wrapper(arg): return extra*func(arg) return wrapper return decorator @boost_by(10) def boosted(arg): return arg*2 assert boosted(10) == 200 """, [1,2,3,4,5,6,8,10,12], "") def test_double_function_decorators(self): self.check_coverage("""\ def require_int(func): def wrapper(arg): assert isinstance(arg, int) return func(arg) return wrapper def boost_by(extra): def decorator(func): def wrapper(arg): return extra*func(arg) return wrapper return decorator @require_int @boost_by(10) def boosted1(arg): return arg*2 assert boosted1(10) == 200 @boost_by(10) @require_int def boosted2(arg): return arg*2 assert boosted2(10) == 200 """, ([1,2,3,4,5,7,8,9,10,11,12,14,15,17,19,21,22,24,26], [1,2,3,4,5,7,8,9,10,11,12,14, 17,19,21, 24,26]), "") class Py25Test(CoverageTest): """Tests of new syntax in Python 2.5.""" def test_with_statement(self): self.check_coverage("""\ class Managed: def __enter__(self): desc = "enter" def __exit__(self, type, value, tb): desc = "exit" m = Managed() with m: desc = "block1a" desc = "block1b" try: with m: desc = "block2" raise Exception("Boo!") except: desc = "caught" """, [1,2,3,5,6,8,9,10,11,13,14,15,16,17,18], "") def test_try_except_finally(self): self.check_coverage("""\ a = 0; b = 0 try: a = 1 except: a = 99 finally: b = 2 assert a == 1 and b == 2 """, [1,2,3,4,5,7,8], "4-5", arcz=".1 12 23 37 45 57 78 8.", arcz_missing="45 57", ) self.check_coverage("""\ a = 0; b = 0 try: a = 1 raise Exception("foo") except: a = 99 finally: b = 2 assert a == 99 and b == 2 """, [1,2,3,4,5,6,8,9], "", arcz=".1 12 23 34 45 56 68 89 9.", ) self.check_coverage("""\ a = 0; b = 0 try: a = 1 raise Exception("foo") except ImportError: a = 99 except: a = 123 finally: b = 2 assert a == 123 and b == 2 """, [1,2,3,4,5,6,7,8,10,11], "6", arcz=".1 12 23 34 45 56 57 78 6A 8A AB B.", arcz_missing="56 6A", ) self.check_coverage("""\ a = 0; b = 0 try: a = 1 raise IOError("foo") except ImportError: a = 99 except IOError: a = 17 except: a = 123 finally: b = 2 assert a == 17 and b == 2 """, [1,2,3,4,5,6,7,8,9,10,12,13], "6, 9-10", arcz=".1 12 23 34 45 56 6C 57 78 8C 79 9A AC CD D.", arcz_missing="56 6C 79 9A AC", ) self.check_coverage("""\ a = 0; b = 0 try: a = 1 except: a = 99 else: a = 123 finally: b = 2 assert a == 123 and b == 2 """, [1,2,3,4,5,7,9,10], "4-5", arcz=".1 12 23 37 45 59 79 9A A.", arcz_missing="45 59", ) self.check_coverage("""\ a = 0; b = 0 try: a = 1 raise Exception("foo") except: a = 99 else: a = 123 finally: b = 2 assert a == 99 and b == 2 """, [1,2,3,4,5,6,8,10,11], "8", arcz=".1 12 23 34 45 56 6A 8A AB B.", arcz_missing="8A", ) class ModuleTest(CoverageTest): """Tests for the module-level behavior of the `coverage` module.""" run_in_temp_dir = False def test_not_singleton(self): # You *can* create another coverage object. coverage.Coverage() coverage.Coverage() def test_old_name_and_new_name(self): self.assertIs(coverage.coverage, coverage.Coverage) class ReportingTest(CoverageTest): """Tests of some reporting behavior.""" # We don't make any temporary files, but we need an empty directory to run # the tests in. no_files_in_temp_dir = True def test_no_data_to_report_on_annotate(self): # Reporting with no data produces a nice message and no output # directory. with self.assertRaisesRegex(CoverageException, "No data to report."): self.command_line("annotate -d ann") self.assert_doesnt_exist("ann") def test_no_data_to_report_on_html(self): # Reporting with no data produces a nice message and no output # directory. with self.assertRaisesRegex(CoverageException, "No data to report."): self.command_line("html -d htmlcov") self.assert_doesnt_exist("htmlcov") def test_no_data_to_report_on_xml(self): # Reporting with no data produces a nice message. with self.assertRaisesRegex(CoverageException, "No data to report."): self.command_line("xml") self.assert_doesnt_exist("coverage.xml") python-coverage-4.5+dfsg.1.orig/tests/modules/0000755000076600000620000000000013235414516017274 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/usepkgs.py0000644000076600000620000000046013173515667021341 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt import pkg1.p1a, pkg1.p1b, pkg1.sub import pkg2.p2a, pkg2.p2b import othermods.othera, othermods.otherb import othermods.sub.osa, othermods.sub.osb python-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/0000755000076600000620000000000013235414516020136 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/p1b.py0000644000076600000620000000025613146037571021200 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt x = 1 y = 2 z = 3 python-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/__init__.py0000644000076600000620000000011613177624110022243 0ustar staff# A simple package for testing with. print("pkg1.__init__: %s" % (__name__,)) python-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/runmod2.py0000644000076600000620000000037313146037571022104 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Used in the tests for run_python_module import sys print("runmod2: passed %s" % sys.argv[1]) python-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/p1c.py0000644000076600000620000000025613146037571021201 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt a = 1 b = 2 c = 3 python-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/sub/0000755000076600000620000000000013235414516020727 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/sub/runmod3.py0000644000076600000620000000037313146037571022676 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Used in the tests for run_python_module import sys print("runmod3: passed %s" % sys.argv[1]) python-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/sub/__init__.py0000644000076600000620000000000013146037571023031 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/sub/ps1a.py0000644000076600000620000000025613146037571022153 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt d = 1 e = 2 f = 3 python-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/sub/__main__.py0000644000076600000620000000015113146037571023021 0ustar staff# Used in the tests for run_python_module import sys print("pkg1.sub.__main__: passed %s" % sys.argv[1]) python-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/__main__.py0000644000076600000620000000014513146037571022233 0ustar staff# Used in the tests for run_python_module import sys print("pkg1.__main__: passed %s" % sys.argv[1]) python-coverage-4.5+dfsg.1.orig/tests/modules/pkg1/p1a.py0000644000076600000620000000044513146037571021177 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt import os, sys # Invoke functions in os and sys so we can see if we measure code there. x = sys.getfilesystemencoding() y = os.getcwd() python-coverage-4.5+dfsg.1.orig/tests/modules/aa/0000755000076600000620000000000013235414516017655 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/aa/bb.odd/0000755000076600000620000000000013235414516021005 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/aa/bb.odd/bfile.py0000644000076600000620000000001313146037571022435 0ustar staff# bfile.py python-coverage-4.5+dfsg.1.orig/tests/modules/aa/__init__.py0000644000076600000620000000000513146037571021764 0ustar staff# aa python-coverage-4.5+dfsg.1.orig/tests/modules/aa/bb/0000755000076600000620000000000013235414516020240 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/aa/bb/__init__.py0000644000076600000620000000000513146037571022347 0ustar staff# bb python-coverage-4.5+dfsg.1.orig/tests/modules/aa/bb/bfile.py0000644000076600000620000000001313146037571021670 0ustar staff# bfile.py python-coverage-4.5+dfsg.1.orig/tests/modules/aa/bb/cc/0000755000076600000620000000000013235414516020625 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/aa/bb/cc/__init__.py0000644000076600000620000000000013146037571022727 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/aa/bb/cc/cfile.py0000644000076600000620000000001313146037571022256 0ustar staff# cfile.py python-coverage-4.5+dfsg.1.orig/tests/modules/aa/bb/bfile.odd.py0000644000076600000620000000001713146037571022441 0ustar staff# bfile.odd.py python-coverage-4.5+dfsg.1.orig/tests/modules/aa/afile.py0000644000076600000620000000001313146037571021304 0ustar staff# afile.py python-coverage-4.5+dfsg.1.orig/tests/modules/aa/zfile.py0000644000076600000620000000001313146037571021335 0ustar staff# zfile.py python-coverage-4.5+dfsg.1.orig/tests/modules/aa/afile.odd.py0000644000076600000620000000001713146037571022055 0ustar staff# afile.odd.py python-coverage-4.5+dfsg.1.orig/tests/modules/namespace_420/0000755000076600000620000000000013235414514021613 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/namespace_420/sub1/0000755000076600000620000000000013235414516022467 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/namespace_420/sub1/__init__.py0000644000076600000620000000027013173515667024611 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt sub1 = "namespace_420 sub1" python-coverage-4.5+dfsg.1.orig/tests/modules/plugins/0000755000076600000620000000000013235414516020755 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/plugins/another.py0000644000076600000620000000052613146037571022775 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """A plugin for tests to reference.""" from coverage import CoveragePlugin class Plugin(CoveragePlugin): pass def coverage_init(reg, options): reg.add_file_tracer(Plugin()) python-coverage-4.5+dfsg.1.orig/tests/modules/plugins/__init__.py0000644000076600000620000000000013146037571023057 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/plugins/a_plugin.py0000644000076600000620000000027213146037571023131 0ustar staff"""A plugin for tests to reference.""" from coverage import CoveragePlugin class Plugin(CoveragePlugin): pass def coverage_init(reg, options): reg.add_file_tracer(Plugin()) python-coverage-4.5+dfsg.1.orig/tests/modules/runmod1.py0000644000076600000620000000037313146037571021241 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Used in the tests for run_python_module import sys print("runmod1: passed %s" % sys.argv[1]) python-coverage-4.5+dfsg.1.orig/tests/modules/process_test/0000755000076600000620000000000013235414516022011 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/process_test/try_execfile.py0000644000076600000620000000551113173515667025061 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Test file for run_python_file. This file is executed two ways:: $ coverage run try_execfile.py and:: $ python try_execfile.py The output is compared to see that the program execution context is the same under coverage and under Python. It is not crucial that the execution be identical, there are some differences that are OK. This program canonicalizes the output to gloss over those differences and get a clean diff. """ import itertools import json import os import sys # sys.path varies by execution environments. Coverage.py uses setuptools to # make console scripts, which means pkg_resources is imported. pkg_resources # removes duplicate entries from sys.path. So we do that too, since the extra # entries don't affect the running of the program. def same_file(p1, p2): """Determine if `p1` and `p2` refer to the same existing file.""" if not p1: return not p2 if not os.path.exists(p1): return False if not os.path.exists(p2): return False if hasattr(os.path, "samefile"): return os.path.samefile(p1, p2) else: norm1 = os.path.normcase(os.path.normpath(p1)) norm2 = os.path.normcase(os.path.normpath(p2)) return norm1 == norm2 def without_same_files(filenames): """Return the list `filenames` with duplicates (by same_file) removed.""" reduced = [] for filename in filenames: if not any(same_file(filename, other) for other in reduced): reduced.append(filename) return reduced cleaned_sys_path = [os.path.normcase(p) for p in without_same_files(sys.path)] DATA = "xyzzy" import __main__ def my_function(a): """A function to force execution of module-level values.""" return "my_fn(%r)" % a FN_VAL = my_function("fooey") loader = globals().get('__loader__') fullname = getattr(loader, 'fullname', None) or getattr(loader, 'name', None) # A more compact grouped-by-first-letter list of builtins. def word_group(w): """Clump AB, CD, EF, etc.""" return chr((ord(w[0]) + 1) & 0xFE) builtin_dir = [" ".join(s) for _, s in itertools.groupby(dir(__builtins__), key=word_group)] globals_to_check = { 'os.getcwd': os.getcwd(), '__name__': __name__, '__file__': __file__, '__doc__': __doc__, '__builtins__.has_open': hasattr(__builtins__, 'open'), '__builtins__.dir': builtin_dir, '__loader__ exists': loader is not None, '__loader__.fullname': fullname, '__package__': __package__, 'DATA': DATA, 'FN_VAL': FN_VAL, '__main__.DATA': getattr(__main__, "DATA", "nothing"), 'argv0': sys.argv[0], 'argv1-n': sys.argv[1:], 'path': cleaned_sys_path, } print(json.dumps(globals_to_check, indent=4, sort_keys=True)) python-coverage-4.5+dfsg.1.orig/tests/modules/process_test/__init__.py0000644000076600000620000000000013146037571024113 0ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/pkg2/0000755000076600000620000000000013235414516020137 5ustar staffpython-coverage-4.5+dfsg.1.orig/tests/modules/pkg2/__init__.py0000644000076600000620000000016113146037571022251 0ustar staff# This is an __init__.py file, with no executable statements in it. # This comment shouldn't confuse the parser. python-coverage-4.5+dfsg.1.orig/tests/modules/pkg2/p2b.py0000644000076600000620000000025613146037571021202 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt t = 1 u = 1 v = 1 python-coverage-4.5+dfsg.1.orig/tests/modules/pkg2/p2a.py0000644000076600000620000000025613146037571021201 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt q = 1 r = 1 s = 1 python-coverage-4.5+dfsg.1.orig/tests/modules/covmod1.py0000644000076600000620000000032413146037571021220 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # covmod1.py: Simplest module for testing. i = 1 i += 1 python-coverage-4.5+dfsg.1.orig/tests/test_misc.py0000644000076600000620000001045313173515667020205 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests of miscellaneous stuff.""" import pytest from coverage.misc import contract, dummy_decorator_with_args, file_be_gone from coverage.misc import format_lines, Hasher, one_of from tests.coveragetest import CoverageTest class HasherTest(CoverageTest): """Test our wrapper of md5 hashing.""" run_in_temp_dir = False def test_string_hashing(self): h1 = Hasher() h1.update("Hello, world!") h2 = Hasher() h2.update("Goodbye!") h3 = Hasher() h3.update("Hello, world!") self.assertNotEqual(h1.hexdigest(), h2.hexdigest()) self.assertEqual(h1.hexdigest(), h3.hexdigest()) def test_bytes_hashing(self): h1 = Hasher() h1.update(b"Hello, world!") h2 = Hasher() h2.update(b"Goodbye!") self.assertNotEqual(h1.hexdigest(), h2.hexdigest()) def test_unicode_hashing(self): h1 = Hasher() h1.update(u"Hello, world! \N{SNOWMAN}") h2 = Hasher() h2.update(u"Goodbye!") self.assertNotEqual(h1.hexdigest(), h2.hexdigest()) def test_dict_hashing(self): h1 = Hasher() h1.update({'a': 17, 'b': 23}) h2 = Hasher() h2.update({'b': 23, 'a': 17}) self.assertEqual(h1.hexdigest(), h2.hexdigest()) class RemoveFileTest(CoverageTest): """Tests of misc.file_be_gone.""" def test_remove_nonexistent_file(self): # It's OK to try to remove a file that doesn't exist. file_be_gone("not_here.txt") def test_remove_actual_file(self): # It really does remove a file that does exist. self.make_file("here.txt", "We are here, we are here, we are here!") file_be_gone("here.txt") self.assert_doesnt_exist("here.txt") def test_actual_errors(self): # Errors can still happen. # ". is a directory" on Unix, or "Access denied" on Windows with self.assertRaises(OSError): file_be_gone(".") class ContractTest(CoverageTest): """Tests of our contract decorators.""" run_in_temp_dir = False def test_bytes(self): @contract(text='bytes|None') def need_bytes(text=None): # pylint: disable=missing-docstring return text assert need_bytes(b"Hey") == b"Hey" assert need_bytes() is None with pytest.raises(Exception): need_bytes(u"Oops") def test_unicode(self): @contract(text='unicode|None') def need_unicode(text=None): # pylint: disable=missing-docstring return text assert need_unicode(u"Hey") == u"Hey" assert need_unicode() is None with pytest.raises(Exception): need_unicode(b"Oops") def test_one_of(self): @one_of("a, b, c") def give_me_one(a=None, b=None, c=None): # pylint: disable=missing-docstring return (a, b, c) assert give_me_one(a=17) == (17, None, None) assert give_me_one(b=set()) == (None, set(), None) assert give_me_one(c=17) == (None, None, 17) with pytest.raises(AssertionError): give_me_one(a=17, b=set()) with pytest.raises(AssertionError): give_me_one() def test_dummy_decorator_with_args(self): @dummy_decorator_with_args("anything", this=17, that="is fine") def undecorated(a=None, b=None): # pylint: disable=missing-docstring return (a, b) assert undecorated() == (None, None) assert undecorated(17) == (17, None) assert undecorated(b=23) == (None, 23) assert undecorated(b=42, a=3) == (3, 42) @pytest.mark.parametrize("statements, lines, result", [ (set([1,2,3,4,5,10,11,12,13,14]), set([1,2,5,10,11,13,14]), "1-2, 5-11, 13-14"), ([1,2,3,4,5,10,11,12,13,14,98,99], [1,2,5,10,11,13,14,99], "1-2, 5-11, 13-14, 99"), ([1,2,3,4,98,99,100,101,102,103,104], [1,2,99,102,103,104], "1-2, 99, 102-104"), ([17], [17], "17"), ([90,91,92,93,94,95], [90,91,92,93,94,95], "90-95"), ([1, 2, 3, 4, 5], [], ""), ([1, 2, 3, 4, 5], [4], "4"), ]) def test_format_lines(statements, lines, result): assert format_lines(statements, lines) == result python-coverage-4.5+dfsg.1.orig/tests/covmodzip1.py0000644000076600000620000000054713177624110020275 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Module-level docstrings are counted differently in different versions of Python, # so don't add one here. # pylint: disable=missing-docstring # covmodzip.py: for putting into a zip file. j = 1 j += 1 python-coverage-4.5+dfsg.1.orig/tests/test_farm.py0000644000076600000620000003063213173515667020200 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Run tests in the farm sub-directory. Designed for pytest.""" import difflib import filecmp import fnmatch import glob import os import re import shutil import sys import pytest from unittest_mixins import ModuleAwareMixin, SysPathAwareMixin, change_dir from tests.helpers import run_command from tests.backtest import execfile # pylint: disable=redefined-builtin from coverage import env from coverage.backunittest import unittest from coverage.debug import _TEST_NAME_FILE # Look for files that become tests. TEST_FILES = glob.glob("tests/farm/*/*.py") @pytest.mark.parametrize("filename", TEST_FILES) def test_farm(filename): if env.JYTHON: # All of the farm tests use reporting, so skip them all. skip("Farm tests don't run on Jython") FarmTestCase(filename).run_fully() # "rU" was deprecated in 3.4 READ_MODE = "rU" if sys.version_info < (3, 4) else "r" class FarmTestCase(ModuleAwareMixin, SysPathAwareMixin, unittest.TestCase): """A test case from the farm tree. Tests are short Python script files, often called run.py: copy("src", "out") run(''' coverage run white.py coverage annotate white.py ''', rundir="out") compare("out", "gold", "*,cover") clean("out") Verbs (copy, run, compare, clean) are methods in this class. FarmTestCase has options to allow various uses of the test cases (normal execution, cleaning-only, or run and leave the results for debugging). This class is a unittest.TestCase so that we can use behavior-modifying mixins, but it's only useful as a test function. Yes, this is confusing. """ # We don't want test runners finding this and instantiating it themselves. __test__ = False def __init__(self, runpy, clean_only=False, dont_clean=False): """Create a test case from a run.py file. `clean_only` means that only the clean() action is executed. `dont_clean` means that the clean() action is not executed. """ super(FarmTestCase, self).__init__() self.description = runpy self.dir, self.runpy = os.path.split(runpy) self.clean_only = clean_only self.dont_clean = dont_clean self.ok = True def setUp(self): """Test set up, run by the test runner before __call__.""" super(FarmTestCase, self).setUp() # Modules should be importable from the current directory. sys.path.insert(0, '') def tearDown(self): """Test tear down, run by the test runner after __call__.""" # Make sure the test is cleaned up, unless we never want to, or if the # test failed. if not self.dont_clean and self.ok: # pragma: part covered self.clean_only = True self() super(FarmTestCase, self).tearDown() # This object will be run via the __call__ method, and test runners # don't do cleanups in that case. Do them now. self.doCleanups() def runTest(self): # pragma: not covered """Here to make unittest.TestCase happy, but will never be invoked.""" raise Exception("runTest isn't used in this class!") def __call__(self): """Execute the test from the run.py file.""" if _TEST_NAME_FILE: # pragma: debugging with open(_TEST_NAME_FILE, "w") as f: f.write(self.description.replace("/", "_")) # Prepare a dictionary of globals for the run.py files to use. fns = """ copy run clean skip compare contains contains_any doesnt_contain """.split() if self.clean_only: glo = dict((fn, noop) for fn in fns) glo['clean'] = clean else: glo = dict((fn, globals()[fn]) for fn in fns) if self.dont_clean: # pragma: debugging glo['clean'] = noop with change_dir(self.dir): try: execfile(self.runpy, glo) except Exception: self.ok = False raise def run_fully(self): """Run as a full test case, with setUp and tearDown.""" self.setUp() try: self() finally: self.tearDown() # Functions usable inside farm run.py files def noop(*args_unused, **kwargs_unused): """A no-op function to stub out run, copy, etc, when only cleaning.""" pass def copy(src, dst): """Copy a directory.""" shutil.copytree(src, dst) def run(cmds, rundir="src", outfile=None): """Run a list of commands. `cmds` is a string, commands separated by newlines. `rundir` is the directory in which to run the commands. `outfile` is a file name to redirect stdout to. """ with change_dir(rundir): if outfile: fout = open(outfile, "a+") try: for cmd in cmds.split("\n"): cmd = cmd.strip() if not cmd: continue retcode, output = run_command(cmd) print(output.rstrip()) if outfile: fout.write(output) if retcode: raise Exception("command exited abnormally") # pragma: only failure finally: if outfile: fout.close() def compare(dir1, dir2, file_pattern=None, size_within=0, left_extra=False, scrubs=None): """Compare files matching `file_pattern` in `dir1` and `dir2`. `size_within` is a percentage delta for the file sizes. If non-zero, then the file contents are not compared (since they are expected to often be different), but the file sizes must be within this amount. For example, size_within=10 means that the two files' sizes must be within 10 percent of each other to compare equal. `left_extra` true means the left directory can have extra files in it without triggering an assertion. `scrubs` is a list of pairs, regexes to find and literal strings to replace them with to scrub the files of unimportant differences. An assertion will be raised if the directories fail one of their matches. """ assert os.path.exists(dir1), "Left directory missing: %s" % dir1 assert os.path.exists(dir2), "Right directory missing: %s" % dir2 dc = filecmp.dircmp(dir1, dir2) diff_files = fnmatch_list(dc.diff_files, file_pattern) left_only = fnmatch_list(dc.left_only, file_pattern) right_only = fnmatch_list(dc.right_only, file_pattern) show_diff = True if size_within: # The files were already compared, use the diff_files list as a # guide for size comparison. wrong_size = [] for f in diff_files: with open(os.path.join(dir1, f), "rb") as fobj: left = fobj.read() with open(os.path.join(dir2, f), "rb") as fobj: right = fobj.read() size_l, size_r = len(left), len(right) big, little = max(size_l, size_r), min(size_l, size_r) if (big - little) / float(little) > size_within/100.0: # print "%d %d" % (big, little) # print "Left: ---\n%s\n-----\n%s" % (left, right) wrong_size.append("%s (%s,%s)" % (f, size_l, size_r)) # pragma: only failure if wrong_size: print("File sizes differ between %s and %s: %s" % ( # pragma: only failure dir1, dir2, ", ".join(wrong_size) )) # We'll show the diff iff the files differed enough in size. show_diff = bool(wrong_size) if show_diff: # filecmp only compares in binary mode, but we want text mode. So # look through the list of different files, and compare them # ourselves. text_diff = [] for f in diff_files: with open(os.path.join(dir1, f), READ_MODE) as fobj: left = fobj.read() with open(os.path.join(dir2, f), READ_MODE) as fobj: right = fobj.read() if scrubs: left = scrub(left, scrubs) right = scrub(right, scrubs) if left != right: # pragma: only failure text_diff.append(f) left = left.splitlines() right = right.splitlines() print("\n".join(difflib.Differ().compare(left, right))) assert not text_diff, "Files differ: %s" % text_diff if not left_extra: assert not left_only, "Files in %s only: %s" % (dir1, left_only) assert not right_only, "Files in %s only: %s" % (dir2, right_only) def contains(filename, *strlist): """Check that the file contains all of a list of strings. An assert will be raised if one of the arguments in `strlist` is missing in `filename`. """ with open(filename, "r") as fobj: text = fobj.read() for s in strlist: assert s in text, "Missing content in %s: %r" % (filename, s) def contains_any(filename, *strlist): """Check that the file contains at least one of a list of strings. An assert will be raised if none of the arguments in `strlist` is in `filename`. """ with open(filename, "r") as fobj: text = fobj.read() for s in strlist: if s in text: return assert False, ( # pragma: only failure "Missing content in %s: %r [1 of %d]" % (filename, strlist[0], len(strlist),) ) def doesnt_contain(filename, *strlist): """Check that the file contains none of a list of strings. An assert will be raised if any of the strings in `strlist` appears in `filename`. """ with open(filename, "r") as fobj: text = fobj.read() for s in strlist: assert s not in text, "Forbidden content in %s: %r" % (filename, s) def clean(cleandir): """Clean `cleandir` by removing it and all its children completely.""" # rmtree gives mysterious failures on Win7, so retry a "few" times. # I've seen it take over 100 tries, so, 1000! This is probably the # most unpleasant hack I've written in a long time... tries = 1000 while tries: # pragma: part covered if os.path.exists(cleandir): try: shutil.rmtree(cleandir) except OSError: # pragma: cant happen if tries == 1: raise else: tries -= 1 continue break def skip(msg=None): """Skip the current test.""" raise unittest.SkipTest(msg) # Helpers def fnmatch_list(files, file_pattern): """Filter the list of `files` to only those that match `file_pattern`. If `file_pattern` is None, then return the entire list of files. Returns a list of the filtered files. """ if file_pattern: files = [f for f in files if fnmatch.fnmatch(f, file_pattern)] return files def scrub(strdata, scrubs): """Scrub uninteresting data from the payload in `strdata`. `scrubs` is a list of (find, replace) pairs of regexes that are used on `strdata`. A string is returned. """ for rgx_find, rgx_replace in scrubs: strdata = re.sub(rgx_find, rgx_replace.replace("\\", "\\\\"), strdata) return strdata def main(): # pragma: debugging """Command-line access to farm tests. Commands: run testcase ... - Run specific test case(s) out testcase ... - Run test cases, but don't clean up, leaving output. clean - Clean all the output for all tests. """ try: op = sys.argv[1] except IndexError: op = 'help' if op == 'run': # Run the test for real. for filename in sys.argv[2:]: FarmTestCase(filename).run_fully() elif op == 'out': # Run the test, but don't clean up, so we can examine the output. for filename in sys.argv[2:]: FarmTestCase(filename, dont_clean=True).run_fully() elif op == 'clean': # Run all the tests, but just clean. for filename in TEST_FILES: FarmTestCase(filename, clean_only=True).run_fully() else: print(main.__doc__) # So that we can run just one farm run.py at a time. if __name__ == '__main__': # pragma: debugging main() python-coverage-4.5+dfsg.1.orig/tests/test_process.py0000644000076600000620000014252313231727101020713 0ustar staff# coding: utf-8 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for process behavior of coverage.py.""" import distutils.sysconfig # pylint: disable=import-error import glob import os import os.path import re import sys import textwrap import pytest import coverage from coverage import env, CoverageData from coverage.misc import output_encoding from tests.coveragetest import CoverageTest from tests.helpers import re_lines class ProcessTest(CoverageTest): """Tests of the per-process behavior of coverage.py.""" def data_files(self): """Return the names of coverage data files in this directory.""" return [f for f in os.listdir('.') if (f.startswith('.coverage.') or f == '.coverage')] def number_of_data_files(self): """Return the number of coverage data files in this directory.""" return len(self.data_files()) def test_save_on_exit(self): self.make_file("mycode.py", """\ h = "Hello" w = "world" """) self.assert_doesnt_exist(".coverage") self.run_command("coverage run mycode.py") self.assert_exists(".coverage") def test_environment(self): # Checks that we can import modules from the tests directory at all! self.make_file("mycode.py", """\ import covmod1 import covmodzip1 a = 1 print('done') """) self.assert_doesnt_exist(".coverage") out = self.run_command("coverage run mycode.py") self.assert_exists(".coverage") self.assertEqual(out, 'done\n') def make_b_or_c_py(self): """Create b_or_c.py, used in a few of these tests.""" self.make_file("b_or_c.py", """\ import sys a = 1 if sys.argv[1] == 'b': b = 1 else: c = 1 d = 1 print('done') """) def test_combine_parallel_data(self): self.make_b_or_c_py() out = self.run_command("coverage run -p b_or_c.py b") self.assertEqual(out, 'done\n') self.assert_doesnt_exist(".coverage") self.assertEqual(self.number_of_data_files(), 1) out = self.run_command("coverage run -p b_or_c.py c") self.assertEqual(out, 'done\n') self.assert_doesnt_exist(".coverage") # After two -p runs, there should be two .coverage.machine.123 files. self.assertEqual(self.number_of_data_files(), 2) # Combine the parallel coverage data files into .coverage . self.run_command("coverage combine") self.assert_exists(".coverage") # After combining, there should be only the .coverage file. self.assertEqual(self.number_of_data_files(), 1) # Read the coverage file and see that b_or_c.py has all 7 lines # executed. data = coverage.CoverageData() data.read_file(".coverage") self.assertEqual(data.line_counts()['b_or_c.py'], 7) # Running combine again should fail, because there are no parallel data # files to combine. status, out = self.run_command_status("coverage combine") self.assertEqual(status, 1) self.assertEqual(out, "No data to combine\n") # And the originally combined data is still there. data = coverage.CoverageData() data.read_file(".coverage") self.assertEqual(data.line_counts()['b_or_c.py'], 7) def test_combine_parallel_data_with_a_corrupt_file(self): self.make_b_or_c_py() out = self.run_command("coverage run -p b_or_c.py b") self.assertEqual(out, 'done\n') self.assert_doesnt_exist(".coverage") self.assertEqual(self.number_of_data_files(), 1) out = self.run_command("coverage run -p b_or_c.py c") self.assertEqual(out, 'done\n') self.assert_doesnt_exist(".coverage") # After two -p runs, there should be two .coverage.machine.123 files. self.assertEqual(self.number_of_data_files(), 2) # Make a bogus data file. self.make_file(".coverage.bad", "This isn't a coverage data file.") # Combine the parallel coverage data files into .coverage . out = self.run_command("coverage combine") self.assert_exists(".coverage") self.assert_exists(".coverage.bad") warning_regex = ( r"Coverage.py warning: Couldn't read data from '.*\.coverage\.bad': " r"CoverageException: Doesn't seem to be a coverage\.py data file" ) self.assertRegex(out, warning_regex) # After combining, those two should be the only data files. self.assertEqual(self.number_of_data_files(), 2) # Read the coverage file and see that b_or_c.py has all 7 lines # executed. data = coverage.CoverageData() data.read_file(".coverage") self.assertEqual(data.line_counts()['b_or_c.py'], 7) def test_combine_no_usable_files(self): # https://bitbucket.org/ned/coveragepy/issues/629/multiple-use-of-combine-leads-to-empty self.make_b_or_c_py() out = self.run_command("coverage run b_or_c.py b") self.assertEqual(out, 'done\n') self.assert_exists(".coverage") self.assertEqual(self.number_of_data_files(), 1) # Make bogus data files. self.make_file(".coverage.bad1", "This isn't a coverage data file.") self.make_file(".coverage.bad2", "This isn't a coverage data file.") # Combine the parallel coverage data files into .coverage, but nothing is readable. status, out = self.run_command_status("coverage combine") self.assertEqual(status, 1) for n in "12": self.assert_exists(".coverage.bad{0}".format(n)) warning_regex = ( r"Coverage.py warning: Couldn't read data from '.*\.coverage\.bad{0}': " r"CoverageException: Doesn't seem to be a coverage\.py data file".format(n) ) self.assertRegex(out, warning_regex) self.assertRegex(out, r"No usable data files") # After combining, we should have a main file and two parallel files. self.assertEqual(self.number_of_data_files(), 3) # Read the coverage file and see that b_or_c.py has 6 lines # executed (we only did b, not c). data = coverage.CoverageData() data.read_file(".coverage") self.assertEqual(data.line_counts()['b_or_c.py'], 6) def test_combine_parallel_data_in_two_steps(self): self.make_b_or_c_py() out = self.run_command("coverage run -p b_or_c.py b") self.assertEqual(out, 'done\n') self.assert_doesnt_exist(".coverage") self.assertEqual(self.number_of_data_files(), 1) # Combine the (one) parallel coverage data file into .coverage . self.run_command("coverage combine") self.assert_exists(".coverage") self.assertEqual(self.number_of_data_files(), 1) out = self.run_command("coverage run -p b_or_c.py c") self.assertEqual(out, 'done\n') self.assert_exists(".coverage") self.assertEqual(self.number_of_data_files(), 2) # Combine the parallel coverage data files into .coverage . self.run_command("coverage combine --append") self.assert_exists(".coverage") # After combining, there should be only the .coverage file. self.assertEqual(self.number_of_data_files(), 1) # Read the coverage file and see that b_or_c.py has all 7 lines # executed. data = coverage.CoverageData() data.read_file(".coverage") self.assertEqual(data.line_counts()['b_or_c.py'], 7) def test_append_data(self): self.make_b_or_c_py() out = self.run_command("coverage run b_or_c.py b") self.assertEqual(out, 'done\n') self.assert_exists(".coverage") self.assertEqual(self.number_of_data_files(), 1) out = self.run_command("coverage run --append b_or_c.py c") self.assertEqual(out, 'done\n') self.assert_exists(".coverage") self.assertEqual(self.number_of_data_files(), 1) # Read the coverage file and see that b_or_c.py has all 7 lines # executed. data = coverage.CoverageData() data.read_file(".coverage") self.assertEqual(data.line_counts()['b_or_c.py'], 7) def test_append_data_with_different_file(self): self.make_b_or_c_py() self.make_file(".coveragerc", """\ [run] data_file = .mycovdata """) out = self.run_command("coverage run b_or_c.py b") self.assertEqual(out, 'done\n') self.assert_doesnt_exist(".coverage") self.assert_exists(".mycovdata") out = self.run_command("coverage run --append b_or_c.py c") self.assertEqual(out, 'done\n') self.assert_doesnt_exist(".coverage") self.assert_exists(".mycovdata") # Read the coverage file and see that b_or_c.py has all 7 lines # executed. data = coverage.CoverageData() data.read_file(".mycovdata") self.assertEqual(data.line_counts()['b_or_c.py'], 7) def test_append_can_create_a_data_file(self): self.make_b_or_c_py() out = self.run_command("coverage run --append b_or_c.py b") self.assertEqual(out, 'done\n') self.assert_exists(".coverage") self.assertEqual(self.number_of_data_files(), 1) # Read the coverage file and see that b_or_c.py has only 6 lines # executed. data = coverage.CoverageData() data.read_file(".coverage") self.assertEqual(data.line_counts()['b_or_c.py'], 6) def test_combine_with_rc(self): self.make_b_or_c_py() self.make_file(".coveragerc", """\ [run] parallel = true """) out = self.run_command("coverage run b_or_c.py b") self.assertEqual(out, 'done\n') self.assert_doesnt_exist(".coverage") out = self.run_command("coverage run b_or_c.py c") self.assertEqual(out, 'done\n') self.assert_doesnt_exist(".coverage") # After two runs, there should be two .coverage.machine.123 files. self.assertEqual(self.number_of_data_files(), 2) # Combine the parallel coverage data files into .coverage . self.run_command("coverage combine") self.assert_exists(".coverage") self.assert_exists(".coveragerc") # After combining, there should be only the .coverage file. self.assertEqual(self.number_of_data_files(), 1) # Read the coverage file and see that b_or_c.py has all 7 lines # executed. data = coverage.CoverageData() data.read_file(".coverage") self.assertEqual(data.line_counts()['b_or_c.py'], 7) # Reporting should still work even with the .rc file out = self.run_command("coverage report") self.assertMultiLineEqual(out, textwrap.dedent("""\ Name Stmts Miss Cover ------------------------------- b_or_c.py 7 0 100% """)) def test_combine_with_aliases(self): self.make_file("d1/x.py", """\ a = 1 b = 2 print("%s %s" % (a, b)) """) self.make_file("d2/x.py", """\ # 1 # 2 # 3 c = 4 d = 5 print("%s %s" % (c, d)) """) self.make_file(".coveragerc", """\ [run] parallel = True [paths] source = src */d1 */d2 """) out = self.run_command("coverage run " + os.path.normpath("d1/x.py")) self.assertEqual(out, '1 2\n') out = self.run_command("coverage run " + os.path.normpath("d2/x.py")) self.assertEqual(out, '4 5\n') self.assertEqual(self.number_of_data_files(), 2) self.run_command("coverage combine") self.assert_exists(".coverage") # After combining, there should be only the .coverage file. self.assertEqual(self.number_of_data_files(), 1) # Read the coverage data file and see that the two different x.py # files have been combined together. data = coverage.CoverageData() data.read_file(".coverage") summary = data.line_counts(fullpath=True) self.assertEqual(len(summary), 1) actual = os.path.normcase(os.path.abspath(list(summary.keys())[0])) expected = os.path.normcase(os.path.abspath('src/x.py')) self.assertEqual(actual, expected) self.assertEqual(list(summary.values())[0], 6) def test_erase_parallel(self): self.make_file(".coveragerc", """\ [run] data_file = data.dat parallel = True """) self.make_file("data.dat") self.make_file("data.dat.fooey") self.make_file("data.dat.gooey") self.make_file(".coverage") self.run_command("coverage erase") self.assert_doesnt_exist("data.dat") self.assert_doesnt_exist("data.dat.fooey") self.assert_doesnt_exist("data.dat.gooey") self.assert_exists(".coverage") def test_missing_source_file(self): # Check what happens if the source is missing when reporting happens. self.make_file("fleeting.py", """\ s = 'goodbye, cruel world!' """) self.run_command("coverage run fleeting.py") os.remove("fleeting.py") out = self.run_command("coverage html -d htmlcov") self.assertRegex(out, "No source for code: '.*fleeting.py'") self.assertNotIn("Traceback", out) # It happens that the code paths are different for *.py and other # files, so try again with no extension. self.make_file("fleeting", """\ s = 'goodbye, cruel world!' """) self.run_command("coverage run fleeting") os.remove("fleeting") status, out = self.run_command_status("coverage html -d htmlcov") self.assertRegex(out, "No source for code: '.*fleeting'") self.assertNotIn("Traceback", out) self.assertEqual(status, 1) def test_running_missing_file(self): status, out = self.run_command_status("coverage run xyzzy.py") self.assertRegex(out, "No file to run: .*xyzzy.py") self.assertNotIn("raceback", out) self.assertNotIn("rror", out) self.assertEqual(status, 1) def test_code_throws(self): self.make_file("throw.py", """\ def f1(): raise Exception("hey!") def f2(): f1() f2() """) # The important thing is for "coverage run" and "python" to report the # same traceback. status, out = self.run_command_status("coverage run throw.py") out2 = self.run_command("python throw.py") if env.PYPY: # Pypy has an extra frame in the traceback for some reason out2 = re_lines(out2, "toplevel", match=False) self.assertMultiLineEqual(out, out2) # But also make sure that the output is what we expect. self.assertIn('File "throw.py", line 5, in f2', out) self.assertIn('raise Exception("hey!")', out) self.assertNotIn('coverage', out) self.assertEqual(status, 1) def test_code_exits(self): self.make_file("exit.py", """\ import sys def f1(): print("about to exit..") sys.exit(17) def f2(): f1() f2() """) # The important thing is for "coverage run" and "python" to have the # same output. No traceback. status, out = self.run_command_status("coverage run exit.py") status2, out2 = self.run_command_status("python exit.py") self.assertMultiLineEqual(out, out2) self.assertMultiLineEqual(out, "about to exit..\n") self.assertEqual(status, status2) self.assertEqual(status, 17) def test_code_exits_no_arg(self): self.make_file("exit_none.py", """\ import sys def f1(): print("about to exit quietly..") sys.exit() f1() """) status, out = self.run_command_status("coverage run exit_none.py") status2, out2 = self.run_command_status("python exit_none.py") self.assertMultiLineEqual(out, out2) self.assertMultiLineEqual(out, "about to exit quietly..\n") self.assertEqual(status, status2) self.assertEqual(status, 0) def test_fork(self): if not hasattr(os, 'fork'): self.skipTest("Can't test os.fork since it doesn't exist.") self.make_file("fork.py", """\ import os def child(): print('Child!') def main(): ret = os.fork() if ret == 0: child() else: os.waitpid(ret, 0) main() """) out = self.run_command("coverage run -p fork.py") self.assertEqual(out, 'Child!\n') self.assert_doesnt_exist(".coverage") # After running the forking program, there should be two # .coverage.machine.123 files. self.assertEqual(self.number_of_data_files(), 2) # The two data files should have different random numbers at the end of # the file name. nums = set(name.rpartition(".")[-1] for name in self.data_files()) self.assertEqual(len(nums), 2, "Same random: %s" % (self.data_files(),)) # Combine the parallel coverage data files into .coverage . self.run_command("coverage combine") self.assert_exists(".coverage") # After combining, there should be only the .coverage file. self.assertEqual(self.number_of_data_files(), 1) # Read the coverage file and see that b_or_c.py has all 7 lines # executed. data = coverage.CoverageData() data.read_file(".coverage") self.assertEqual(data.line_counts()['fork.py'], 9) def test_warnings_during_reporting(self): # While fixing issue #224, the warnings were being printed far too # often. Make sure they're not any more. self.make_file("hello.py", """\ import sys, os, the_other print("Hello") """) self.make_file("the_other.py", """\ print("What?") """) self.make_file(".coveragerc", """\ [run] source = . xyzzy """) self.run_command("coverage run hello.py") out = self.run_command("coverage html") self.assertEqual(out.count("Module xyzzy was never imported."), 0) def test_warnings_if_never_run(self): out = self.run_command("coverage run i_dont_exist.py") self.assertIn("No file to run: 'i_dont_exist.py'", out) self.assertNotIn("warning", out) self.assertNotIn("Exception", out) out = self.run_command("coverage run -m no_such_module") self.assertTrue( ("No module named no_such_module" in out) or ("No module named 'no_such_module'" in out) ) self.assertNotIn("warning", out) self.assertNotIn("Exception", out) def test_warnings_trace_function_changed_with_threads(self): # https://bitbucket.org/ned/coveragepy/issue/164 self.make_file("bug164.py", """\ import threading import time class MyThread (threading.Thread): def run(self): print("Hello") thr = MyThread() thr.start() thr.join() """) out = self.run_command("coverage run --timid bug164.py") self.assertIn("Hello\n", out) self.assertNotIn("warning", out) def test_warning_trace_function_changed(self): self.make_file("settrace.py", """\ import sys print("Hello") sys.settrace(None) print("Goodbye") """) out = self.run_command("coverage run --timid settrace.py") self.assertIn("Hello\n", out) self.assertIn("Goodbye\n", out) self.assertIn("Trace function changed", out) def test_note(self): if env.PYPY and env.PY3 and env.PYPYVERSION[:3] == (5, 10, 0): # https://bitbucket.org/pypy/pypy/issues/2729/pypy3-510-incorrectly-decodes-astral-plane self.skipTest("Avoid incorrect decoding astral plane JSON chars") self.make_file(".coveragerc", """\ [run] data_file = mydata.dat note = These are musical notes: ♫𝅗𝅥♩ """) self.make_file("simple.py", """print('hello')""") self.run_command("coverage run simple.py") data = CoverageData() data.read_file("mydata.dat") infos = data.run_infos() self.assertEqual(len(infos), 1) expected = u"These are musical notes: ♫𝅗𝅥♩" self.assertEqual(infos[0]['note'], expected) @pytest.mark.expensive def test_fullcoverage(self): # pragma: no metacov if env.PY2: # This doesn't work on Python 2. self.skipTest("fullcoverage doesn't work on Python 2.") # It only works with the C tracer, and if we aren't measuring ourselves. if not env.C_TRACER or env.METACOV: self.skipTest("fullcoverage only works with the C tracer.") # fullcoverage is a trick to get stdlib modules measured from # the very beginning of the process. Here we import os and # then check how many lines are measured. self.make_file("getenv.py", """\ import os print("FOOEY == %s" % os.getenv("FOOEY")) """) fullcov = os.path.join( os.path.dirname(coverage.__file__), "fullcoverage" ) self.set_environ("FOOEY", "BOO") self.set_environ("PYTHONPATH", fullcov) out = self.run_command("python -m coverage run -L getenv.py") self.assertEqual(out, "FOOEY == BOO\n") data = coverage.CoverageData() data.read_file(".coverage") # The actual number of executed lines in os.py when it's # imported is 120 or so. Just running os.getenv executes # about 5. self.assertGreater(data.line_counts()['os.py'], 50) def test_lang_c(self): if env.PY3 and sys.version_info < (3, 4): # Python 3.3 can't compile the non-ascii characters in the file name. self.skipTest("3.3 can't handle this test") if env.JYTHON: # Jython as of 2.7.1rc3 won't compile a filename that isn't utf8. self.skipTest("Jython can't handle this test") # LANG=C forces getfilesystemencoding on Linux to 'ascii', which causes # failures with non-ascii file names. We don't want to make a real file # with strange characters, though, because that gets the test runners # tangled up. This will isolate the concerns to the coverage.py code. # https://bitbucket.org/ned/coveragepy/issues/533/exception-on-unencodable-file-name self.make_file("weird_file.py", r""" globs = {} code = "a = 1\nb = 2\n" exec(compile(code, "wut\xe9\xea\xeb\xec\x01\x02.py", 'exec'), globs) print(globs['a']) print(globs['b']) """) self.set_environ("LANG", "C") out = self.run_command("coverage run weird_file.py") self.assertEqual(out, "1\n2\n") def test_deprecation_warnings(self): # Test that coverage doesn't trigger deprecation warnings. # https://bitbucket.org/ned/coveragepy/issue/305/pendingdeprecationwarning-the-imp-module self.make_file("allok.py", """\ import warnings warnings.simplefilter('default') import coverage print("No warnings!") """) out = self.run_command("python allok.py") self.assertEqual(out, "No warnings!\n") def test_run_twice(self): # https://bitbucket.org/ned/coveragepy/issue/353/40a3-introduces-an-unexpected-third-case self.make_file("foo.py", """\ def foo(): pass """) self.make_file("run_twice.py", """\ import coverage for _ in [1, 2]: inst = coverage.Coverage(source=['foo']) inst.load() inst.start() import foo inst.stop() inst.save() """) out = self.run_command("python run_twice.py") self.assertEqual( out, "Coverage.py warning: Module foo was previously imported, but not measured " "(module-not-measured)\n" ) def test_module_name(self): if sys.version_info < (2, 7): # Python 2.6 thinks that coverage is a package that can't be # executed self.skipTest("-m doesn't work the same < Python 2.7") # https://bitbucket.org/ned/coveragepy/issues/478/help-shows-silly-program-name-when-running out = self.run_command("python -m coverage") self.assertIn("Use 'coverage help' for help", out) TRY_EXECFILE = os.path.join(os.path.dirname(__file__), "modules/process_test/try_execfile.py") class EnvironmentTest(CoverageTest): """Tests using try_execfile.py to test the execution environment.""" def assert_tryexecfile_output(self, out1, out2): """Assert that the output we got is a successful run of try_execfile.py. `out1` and `out2` must be the same, modulo a few slight known platform differences. """ # First, is this even credible try_execfile.py output? self.assertIn('"DATA": "xyzzy"', out1) if env.JYTHON: # pragma: only jython # Argv0 is different for Jython, remove that from the comparison. out1 = re_lines(out1, r'\s+"argv0":', match=False) out2 = re_lines(out2, r'\s+"argv0":', match=False) self.assertMultiLineEqual(out1, out2) def test_coverage_run_is_like_python(self): with open(TRY_EXECFILE) as f: self.make_file("run_me.py", f.read()) out_cov = self.run_command("coverage run run_me.py") out_py = self.run_command("python run_me.py") self.assert_tryexecfile_output(out_cov, out_py) def test_coverage_run_dashm_is_like_python_dashm(self): # These -m commands assume the coverage tree is on the path. out_cov = self.run_command("coverage run -m process_test.try_execfile") out_py = self.run_command("python -m process_test.try_execfile") self.assert_tryexecfile_output(out_cov, out_py) def test_coverage_run_dir_is_like_python_dir(self): if sys.version_info == (3, 5, 4, 'final', 0): self.skipTest("3.5.4 broke this: https://bugs.python.org/issue32551") with open(TRY_EXECFILE) as f: self.make_file("with_main/__main__.py", f.read()) out_cov = self.run_command("coverage run with_main") out_py = self.run_command("python with_main") # The coverage.py results are not identical to the Python results, and # I don't know why. For now, ignore those failures. If someone finds # a real problem with the discrepancies, we can work on it some more. ignored = r"__file__|__loader__|__package__" # PyPy includes the current directory in the path when running a # directory, while CPython and coverage.py do not. Exclude that from # the comparison also... if env.PYPY: ignored += "|"+re.escape(os.getcwd()) out_cov = re_lines(out_cov, ignored, match=False) out_py = re_lines(out_py, ignored, match=False) self.assert_tryexecfile_output(out_cov, out_py) def test_coverage_run_dashm_equal_to_doubledashsource(self): """regression test for #328 When imported by -m, a module's __name__ is __main__, but we need the --source machinery to know and respect the original name. """ # These -m commands assume the coverage tree is on the path. out_cov = self.run_command( "coverage run --source process_test.try_execfile -m process_test.try_execfile" ) out_py = self.run_command("python -m process_test.try_execfile") self.assert_tryexecfile_output(out_cov, out_py) def test_coverage_run_dashm_superset_of_doubledashsource(self): """Edge case: --source foo -m foo.bar""" # These -m commands assume the coverage tree is on the path. out_cov = self.run_command( "coverage run --source process_test -m process_test.try_execfile" ) out_py = self.run_command("python -m process_test.try_execfile") self.assert_tryexecfile_output(out_cov, out_py) st, out = self.run_command_status("coverage report") self.assertEqual(st, 0) self.assertEqual(self.line_count(out), 6, out) def test_coverage_run_script_imports_doubledashsource(self): # This file imports try_execfile, which compiles it to .pyc, so the # first run will have __file__ == "try_execfile.py" and the second will # have __file__ == "try_execfile.pyc", which throws off the comparison. # Setting dont_write_bytecode True stops the compilation to .pyc and # keeps the test working. self.make_file("myscript", """\ import sys; sys.dont_write_bytecode = True import process_test.try_execfile """) # These -m commands assume the coverage tree is on the path. out_cov = self.run_command( "coverage run --source process_test myscript" ) out_py = self.run_command("python myscript") self.assert_tryexecfile_output(out_cov, out_py) st, out = self.run_command_status("coverage report") self.assertEqual(st, 0) self.assertEqual(self.line_count(out), 6, out) def test_coverage_run_dashm_is_like_python_dashm_off_path(self): # https://bitbucket.org/ned/coveragepy/issue/242 self.make_file("sub/__init__.py", "") with open(TRY_EXECFILE) as f: self.make_file("sub/run_me.py", f.read()) out_cov = self.run_command("coverage run -m sub.run_me") out_py = self.run_command("python -m sub.run_me") self.assert_tryexecfile_output(out_cov, out_py) def test_coverage_run_dashm_is_like_python_dashm_with__main__207(self): if sys.version_info < (2, 7): # Coverage.py isn't bug-for-bug compatible in the behavior # of -m for Pythons < 2.7 self.skipTest("-m doesn't work the same < Python 2.7") # https://bitbucket.org/ned/coveragepy/issue/207 self.make_file("package/__init__.py", "print('init')") self.make_file("package/__main__.py", "print('main')") out_cov = self.run_command("coverage run -m package") out_py = self.run_command("python -m package") self.assertMultiLineEqual(out_cov, out_py) class ExcepthookTest(CoverageTest): """Tests of sys.excepthook support.""" def test_excepthook(self): self.make_file("excepthook.py", """\ import sys def excepthook(*args): print('in excepthook') if maybe == 2: print('definitely') sys.excepthook = excepthook maybe = 1 raise RuntimeError('Error Outside') """) cov_st, cov_out = self.run_command_status("coverage run excepthook.py") py_st, py_out = self.run_command_status("python excepthook.py") if not env.JYTHON: self.assertEqual(cov_st, py_st) self.assertEqual(cov_st, 1) self.assertIn("in excepthook", py_out) self.assertEqual(cov_out, py_out) # Read the coverage file and see that excepthook.py has 7 lines # executed. data = coverage.CoverageData() data.read_file(".coverage") self.assertEqual(data.line_counts()['excepthook.py'], 7) def test_excepthook_exit(self): if env.PYPY or env.JYTHON: self.skipTest("non-CPython handles excepthook exits differently, punt for now.") self.make_file("excepthook_exit.py", """\ import sys def excepthook(*args): print('in excepthook') sys.exit(0) sys.excepthook = excepthook raise RuntimeError('Error Outside') """) cov_st, cov_out = self.run_command_status("coverage run excepthook_exit.py") py_st, py_out = self.run_command_status("python excepthook_exit.py") self.assertEqual(cov_st, py_st) self.assertEqual(cov_st, 0) self.assertIn("in excepthook", py_out) self.assertEqual(cov_out, py_out) def test_excepthook_throw(self): if env.PYPY: self.skipTest("PyPy handles excepthook throws differently, punt for now.") self.make_file("excepthook_throw.py", """\ import sys def excepthook(*args): # Write this message to stderr so that we don't have to deal # with interleaved stdout/stderr comparisons in the assertions # in the test. sys.stderr.write('in excepthook\\n') raise RuntimeError('Error Inside') sys.excepthook = excepthook raise RuntimeError('Error Outside') """) cov_st, cov_out = self.run_command_status("coverage run excepthook_throw.py") py_st, py_out = self.run_command_status("python excepthook_throw.py") if not env.JYTHON: self.assertEqual(cov_st, py_st) self.assertEqual(cov_st, 1) self.assertIn("in excepthook", py_out) self.assertEqual(cov_out, py_out) class AliasedCommandTest(CoverageTest): """Tests of the version-specific command aliases.""" run_in_temp_dir = False def setUp(self): super(AliasedCommandTest, self).setUp() if env.JYTHON: self.skipTest("Coverage command names don't work on Jython") def test_major_version_works(self): # "coverage2" works on py2 cmd = "coverage%d" % sys.version_info[0] out = self.run_command(cmd) self.assertIn("Code coverage for Python", out) def test_wrong_alias_doesnt_work(self): # "coverage3" doesn't work on py2 assert sys.version_info[0] in [2, 3] # Let us know when Python 4 is out... badcmd = "coverage%d" % (5 - sys.version_info[0]) out = self.run_command(badcmd) self.assertNotIn("Code coverage for Python", out) def test_specific_alias_works(self): # "coverage-2.7" works on py2.7 cmd = "coverage-%d.%d" % sys.version_info[:2] out = self.run_command(cmd) self.assertIn("Code coverage for Python", out) def test_aliases_used_in_messages(self): cmds = [ "coverage", "coverage%d" % sys.version_info[0], "coverage-%d.%d" % sys.version_info[:2], ] for cmd in cmds: out = self.run_command("%s foobar" % cmd) self.assertIn("Unknown command: 'foobar'", out) self.assertIn("Use '%s help' for help" % cmd, out) class PydocTest(CoverageTest): """Test that pydoc can get our information.""" run_in_temp_dir = False def assert_pydoc_ok(self, name, thing): """Check that pydoc of `name` finds the docstring from `thing`.""" # Run pydoc. out = self.run_command("python -m pydoc " + name) # It should say "Help on..", and not have a traceback self.assert_starts_with(out, "Help on ") self.assertNotIn("Traceback", out) # All of the lines in the docstring should be there somewhere. for line in thing.__doc__.splitlines(): self.assertIn(line.strip(), out) def test_pydoc_coverage(self): self.assert_pydoc_ok("coverage", coverage) def test_pydoc_coverage_coverage(self): self.assert_pydoc_ok("coverage.Coverage", coverage.Coverage) class FailUnderTest(CoverageTest): """Tests of the --fail-under switch.""" def setUp(self): super(FailUnderTest, self).setUp() self.make_file("forty_two_plus.py", """\ # I have 42.857% (3/7) coverage! a = 1 b = 2 if a > 3: b = 4 c = 5 d = 6 e = 7 """) st, _ = self.run_command_status("coverage run forty_two_plus.py") self.assertEqual(st, 0) def test_report_43_is_ok(self): st, out = self.run_command_status("coverage report --fail-under=43") self.assertEqual(st, 0) self.assertEqual(self.last_line_squeezed(out), "forty_two_plus.py 7 4 43%") def test_report_43_is_not_ok(self): st, out = self.run_command_status("coverage report --fail-under=44") self.assertEqual(st, 2) self.assertEqual(self.last_line_squeezed(out), "forty_two_plus.py 7 4 43%") def test_report_42p86_is_not_ok(self): self.make_file(".coveragerc", "[report]\nprecision = 2") st, out = self.run_command_status("coverage report --fail-under=42.88") self.assertEqual(st, 2) self.assertEqual(self.last_line_squeezed(out), "forty_two_plus.py 7 4 42.86%") class FailUnderNoFilesTest(CoverageTest): """Test that nothing to report results in an error exit status.""" def test_report(self): self.make_file(".coveragerc", "[report]\nfail_under = 99\n") st, out = self.run_command_status("coverage report") self.assertIn('No data to report.', out) self.assertEqual(st, 1) class FailUnderEmptyFilesTest(CoverageTest): """Test that empty files produce the proper fail_under exit status.""" def test_report(self): self.make_file(".coveragerc", "[report]\nfail_under = 99\n") self.make_file("empty.py", "") st, _ = self.run_command_status("coverage run empty.py") self.assertEqual(st, 0) st, _ = self.run_command_status("coverage report") self.assertEqual(st, 2) class UnicodeFilePathsTest(CoverageTest): """Tests of using non-ascii characters in the names of files.""" def setUp(self): super(UnicodeFilePathsTest, self).setUp() if env.JYTHON: self.skipTest("Jython doesn't like accented file names") def test_accented_dot_py(self): # Make a file with a non-ascii character in the filename. self.make_file(u"h\xe2t.py", "print('accented')") out = self.run_command(u"coverage run h\xe2t.py") self.assertEqual(out, "accented\n") # The HTML report uses ascii-encoded HTML entities. out = self.run_command("coverage html") self.assertEqual(out, "") self.assert_exists(u"htmlcov/h\xe2t_py.html") with open("htmlcov/index.html") as indexf: index = indexf.read() self.assertIn('hât.py', index) # The XML report is always UTF8-encoded. out = self.run_command("coverage xml") self.assertEqual(out, "") with open("coverage.xml", "rb") as xmlf: xml = xmlf.read() self.assertIn(u' filename="h\xe2t.py"'.encode('utf8'), xml) self.assertIn(u' name="h\xe2t.py"'.encode('utf8'), xml) report_expected = ( u"Name Stmts Miss Cover\n" u"----------------------------\n" u"h\xe2t.py 1 0 100%\n" ) if env.PY2: report_expected = report_expected.encode(output_encoding()) out = self.run_command("coverage report") self.assertEqual(out, report_expected) def test_accented_directory(self): # Make a file with a non-ascii character in the directory name. self.make_file(u"\xe2/accented.py", "print('accented')") out = self.run_command(u"coverage run \xe2/accented.py") self.assertEqual(out, "accented\n") # The HTML report uses ascii-encoded HTML entities. out = self.run_command("coverage html") self.assertEqual(out, "") self.assert_exists(u"htmlcov/\xe2_accented_py.html") with open("htmlcov/index.html") as indexf: index = indexf.read() self.assertIn('â%saccented.py' % os.sep, index) # The XML report is always UTF8-encoded. out = self.run_command("coverage xml") self.assertEqual(out, "") with open("coverage.xml", "rb") as xmlf: xml = xmlf.read() self.assertIn(u' filename="\xe2/accented.py"'.encode('utf8'), xml) self.assertIn(u' name="accented.py"'.encode('utf8'), xml) self.assertIn( u''.encode('utf8'), xml ) report_expected = ( u"Name Stmts Miss Cover\n" u"-----------------------------------\n" u"\xe2%saccented.py 1 0 100%%\n" % os.sep ) if env.PY2: report_expected = report_expected.encode(output_encoding()) out = self.run_command("coverage report") self.assertEqual(out, report_expected) def possible_pth_dirs(): """Produce a sequence of directories for trying to write .pth files.""" # First look through sys.path, and if we find a .pth file, then it's a good # place to put ours. for pth_dir in sys.path: # pragma: part covered pth_files = glob.glob(os.path.join(pth_dir, "*.pth")) if pth_files: yield pth_dir # If we're still looking, then try the Python library directory. # https://bitbucket.org/ned/coveragepy/issue/339/pth-test-malfunctions yield distutils.sysconfig.get_python_lib() # pragma: cant happen def find_writable_pth_directory(): """Find a place to write a .pth file.""" for pth_dir in possible_pth_dirs(): # pragma: part covered try_it = os.path.join(pth_dir, "touch_{0}.it".format(WORKER)) with open(try_it, "w") as f: try: f.write("foo") except (IOError, OSError): # pragma: cant happen continue os.remove(try_it) return pth_dir return None # pragma: cant happen WORKER = os.environ.get('PYTEST_XDIST_WORKER', '') PTH_DIR = find_writable_pth_directory() class ProcessCoverageMixin(object): """Set up a .pth file to coverage-measure all sub-processes.""" def setUp(self): super(ProcessCoverageMixin, self).setUp() # Create the .pth file. self.assertTrue(PTH_DIR) pth_contents = "import coverage; coverage.process_startup()\n" pth_path = os.path.join(PTH_DIR, "subcover_{0}.pth".format(WORKER)) with open(pth_path, "w") as pth: pth.write(pth_contents) self.pth_path = pth_path self.addCleanup(os.remove, self.pth_path) class ProcessStartupTest(ProcessCoverageMixin, CoverageTest): """Test that we can measure coverage in sub-processes.""" def setUp(self): super(ProcessStartupTest, self).setUp() # Main will run sub.py self.make_file("main.py", """\ import os, os.path, sys ex = os.path.basename(sys.executable) os.system(ex + " sub.py") """) # sub.py will write a few lines. self.make_file("sub.py", """\ f = open("out.txt", "w") f.write("Hello, world!\\n") f.close() """) def test_subprocess_with_pth_files(self): # pragma: no metacov if env.METACOV: self.skipTest("Can't test sub-process pth file suppport during metacoverage") # An existing data file should not be read when a subprocess gets # measured automatically. Create the data file here with bogus data in # it. data = coverage.CoverageData() data.add_lines({os.path.abspath('sub.py'): dict.fromkeys(range(100))}) data.write_file(".mycovdata") self.make_file("coverage.ini", """\ [run] data_file = .mycovdata """) self.set_environ("COVERAGE_PROCESS_START", "coverage.ini") import main # pylint: disable=import-error, unused-variable with open("out.txt") as f: self.assertEqual(f.read(), "Hello, world!\n") # Read the data from .coverage self.assert_exists(".mycovdata") data = coverage.CoverageData() data.read_file(".mycovdata") self.assertEqual(data.line_counts()['sub.py'], 3) def test_subprocess_with_pth_files_and_parallel(self): # pragma: no metacov # https://bitbucket.org/ned/coveragepy/issues/492/subprocess-coverage-strange-detection-of if env.METACOV: self.skipTest("Can't test sub-process pth file suppport during metacoverage") self.make_file("coverage.ini", """\ [run] parallel = true """) self.set_environ("COVERAGE_PROCESS_START", "coverage.ini") self.run_command("coverage run main.py") with open("out.txt") as f: self.assertEqual(f.read(), "Hello, world!\n") self.run_command("coverage combine") # assert that the combined .coverage data file is correct self.assert_exists(".coverage") data = coverage.CoverageData() data.read_file(".coverage") self.assertEqual(data.line_counts()['sub.py'], 3) # assert that there are *no* extra data files left over after a combine data_files = glob.glob(os.getcwd() + '/.coverage*') self.assertEqual(len(data_files), 1, "Expected only .coverage after combine, looks like there are " "extra data files that were not cleaned up: %r" % data_files) class ProcessStartupWithSourceTest(ProcessCoverageMixin, CoverageTest): """Show that we can configure {[run]source} during process-level coverage. There are three interesting variables, for a total of eight tests: 1. -m versus a simple script argument (for example, `python myscript`), 2. filtering for the top-level (main.py) or second-level (sub.py) module, and 3. whether the files are in a package or not. """ def assert_pth_and_source_work_together( self, dashm, package, source ): # pragma: no metacov """Run the test for a particular combination of factors. The arguments are all strings: * `dashm`: Either "" (run the program as a file) or "-m" (run the program as a module). * `package`: Either "" (put the source at the top level) or a package name to use to hold the source. * `source`: Either "main" or "sub", which file to use as the ``--source`` argument. """ if env.METACOV: self.skipTest("Can't test sub-process pth file suppport during metacoverage") def fullname(modname): """What is the full module name for `modname` for this test?""" if package and dashm: return '.'.join((package, modname)) else: return modname def path(basename): """Where should `basename` be created for this test?""" return os.path.join(package, basename) # Main will run sub.py. self.make_file(path("main.py"), """\ import %s a = 2 b = 3 """ % fullname('sub')) if package: self.make_file(path("__init__.py"), "") # sub.py will write a few lines. self.make_file(path("sub.py"), """\ # Avoid 'with' so Jython can play along. f = open("out.txt", "w") f.write("Hello, world!") f.close() """) self.make_file("coverage.ini", """\ [run] source = %s """ % fullname(source)) self.set_environ("COVERAGE_PROCESS_START", "coverage.ini") if dashm: cmd = "python -m %s" % fullname('main') else: cmd = "python %s" % path('main.py') self.run_command(cmd) with open("out.txt") as f: self.assertEqual(f.read(), "Hello, world!") # Read the data from .coverage self.assert_exists(".coverage") data = coverage.CoverageData() data.read_file(".coverage") summary = data.line_counts() print(summary) self.assertEqual(summary[source + '.py'], 3) self.assertEqual(len(summary), 1) def test_dashm_main(self): self.assert_pth_and_source_work_together('-m', '', 'main') def test_script_main(self): self.assert_pth_and_source_work_together('', '', 'main') def test_dashm_sub(self): self.assert_pth_and_source_work_together('-m', '', 'sub') def test_script_sub(self): self.assert_pth_and_source_work_together('', '', 'sub') def test_dashm_pkg_main(self): self.assert_pth_and_source_work_together('-m', 'pkg', 'main') def test_script_pkg_main(self): self.assert_pth_and_source_work_together('', 'pkg', 'main') def test_dashm_pkg_sub(self): self.assert_pth_and_source_work_together('-m', 'pkg', 'sub') def test_script_pkg_sub(self): self.assert_pth_and_source_work_together('', 'pkg', 'sub') python-coverage-4.5+dfsg.1.orig/tests/test_xml.py0000644000076600000620000003506413173515667020057 0ustar staff# coding: utf-8 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for XML reports from coverage.py.""" import os import os.path import re import coverage from coverage.backward import import_local_file from coverage.files import abs_file from tests.coveragetest import CoverageTest from tests.goldtest import CoverageGoldTest from tests.goldtest import change_dir, compare from tests.helpers import re_line, re_lines class XmlTestHelpers(CoverageTest): """Methods to use from XML tests.""" def run_mycode(self): """Run mycode.py, so we can report on it.""" self.make_file("mycode.py", "print('hello')\n") self.run_command("coverage run mycode.py") def run_doit(self): """Construct a simple sub-package.""" self.make_file("sub/__init__.py") self.make_file("sub/doit.py", "print('doit!')") self.make_file("main.py", "import sub.doit") cov = coverage.Coverage() self.start_import_stop(cov, "main") return cov def make_tree(self, width, depth, curdir="."): """Make a tree of packages. Makes `width` directories, named d0 .. d{width-1}. Each directory has __init__.py, and `width` files, named f0.py .. f{width-1}.py. Each directory also has `width` sub-directories, in the same fashion, until a depth of `depth` is reached. """ if depth == 0: return def here(p): """A path for `p` in our currently interesting directory.""" return os.path.join(curdir, p) for i in range(width): next_dir = here("d{0}".format(i)) self.make_tree(width, depth-1, next_dir) if curdir != ".": self.make_file(here("__init__.py"), "") for i in range(width): filename = here("f{0}.py".format(i)) self.make_file(filename, "# {0}\n".format(filename)) def assert_source(self, xml, src): """Assert that the XML has a element with `src`.""" src = abs_file(src) self.assertRegex(xml, r'\s*{0}\s*'.format(re.escape(src))) class XmlReportTest(XmlTestHelpers, CoverageTest): """Tests of the XML reports from coverage.py.""" def test_default_file_placement(self): self.run_mycode() self.run_command("coverage xml") self.assert_exists("coverage.xml") def test_argument_affects_xml_placement(self): self.run_mycode() self.run_command("coverage xml -o put_it_there.xml") self.assert_doesnt_exist("coverage.xml") self.assert_exists("put_it_there.xml") def test_config_file_directory_does_not_exist(self): self.run_mycode() self.run_command("coverage xml -o nonexistent/put_it_there.xml") self.assert_doesnt_exist("coverage.xml") self.assert_doesnt_exist("put_it_there.xml") self.assert_exists("nonexistent/put_it_there.xml") def test_config_affects_xml_placement(self): self.run_mycode() self.make_file(".coveragerc", "[xml]\noutput = xml.out\n") self.run_command("coverage xml") self.assert_doesnt_exist("coverage.xml") self.assert_exists("xml.out") def test_no_data(self): # https://bitbucket.org/ned/coveragepy/issue/210 self.run_command("coverage xml") self.assert_doesnt_exist("coverage.xml") def test_no_source(self): # Written while investigating a bug, might as well keep it. # https://bitbucket.org/ned/coveragepy/issue/208 self.make_file("innocuous.py", "a = 4") cov = coverage.Coverage() self.start_import_stop(cov, "innocuous") os.remove("innocuous.py") cov.xml_report(ignore_errors=True) self.assert_exists("coverage.xml") def test_filename_format_showing_everything(self): cov = self.run_doit() cov.xml_report(outfile="-") xml = self.stdout() doit_line = re_line(xml, "class.*doit") self.assertIn('filename="sub/doit.py"', doit_line) def test_filename_format_including_filename(self): cov = self.run_doit() cov.xml_report(["sub/doit.py"], outfile="-") xml = self.stdout() doit_line = re_line(xml, "class.*doit") self.assertIn('filename="sub/doit.py"', doit_line) def test_filename_format_including_module(self): cov = self.run_doit() import sub.doit # pylint: disable=import-error cov.xml_report([sub.doit], outfile="-") xml = self.stdout() doit_line = re_line(xml, "class.*doit") self.assertIn('filename="sub/doit.py"', doit_line) def test_reporting_on_nothing(self): # Used to raise a zero division error: # https://bitbucket.org/ned/coveragepy/issue/250 self.make_file("empty.py", "") cov = coverage.Coverage() empty = self.start_import_stop(cov, "empty") cov.xml_report([empty], outfile="-") xml = self.stdout() empty_line = re_line(xml, "class.*empty") self.assertIn('filename="empty.py"', empty_line) self.assertIn('line-rate="1"', empty_line) def test_empty_file_is_100_not_0(self): # https://bitbucket.org/ned/coveragepy/issue/345 cov = self.run_doit() cov.xml_report(outfile="-") xml = self.stdout() init_line = re_line(xml, 'filename="sub/__init__.py"') self.assertIn('line-rate="1"', init_line) def test_curdir_source(self): # With no source= option, the XML report should explain that the source # is in the current directory. cov = self.run_doit() cov.xml_report(outfile="-") xml = self.stdout() self.assert_source(xml, ".") self.assertEqual(xml.count(''), 1) def test_deep_source(self): # When using source=, the XML report needs to mention those directories # in the elements. # https://bitbucket.org/ned/coveragepy/issues/439/incorrect-cobertura-file-sources-generated self.make_file("src/main/foo.py", "a = 1") self.make_file("also/over/there/bar.py", "b = 2") cov = coverage.Coverage(source=["src/main", "also/over/there", "not/really"]) cov.start() mod_foo = import_local_file("foo", "src/main/foo.py") # pragma: nested mod_bar = import_local_file("bar", "also/over/there/bar.py") # pragma: nested cov.stop() # pragma: nested cov.xml_report([mod_foo, mod_bar], outfile="-") xml = self.stdout() self.assert_source(xml, "src/main") self.assert_source(xml, "also/over/there") self.assertEqual(xml.count(''), 2) self.assertIn( '', xml ) self.assertIn( '', xml ) def test_nonascii_directory(self): # https://bitbucket.org/ned/coveragepy/issues/573/cant-generate-xml-report-if-some-source self.make_file("테스트/program.py", "a = 1") with change_dir("테스트"): cov = coverage.Coverage() self.start_import_stop(cov, "program") cov.xml_report() class XmlPackageStructureTest(XmlTestHelpers, CoverageTest): """Tests about the package structure reported in the coverage.xml file.""" def package_and_class_tags(self, cov): """Run an XML report on `cov`, and get the package and class tags.""" self.captured_stdout.truncate(0) cov.xml_report(outfile="-") packages_and_classes = re_lines(self.stdout(), r" """) def test_package_depth(self): self.make_tree(width=1, depth=4) self.make_file("main.py", """\ from d0.d0 import f0 """) cov = coverage.Coverage(source=".") self.start_import_stop(cov, "main") cov.set_option("xml:package_depth", 1) self.assert_package_and_class_tags(cov, """\ """) cov.set_option("xml:package_depth", 2) self.assert_package_and_class_tags(cov, """\ """) cov.set_option("xml:package_depth", 3) self.assert_package_and_class_tags(cov, """\ """) def test_source_prefix(self): # https://bitbucket.org/ned/coveragepy/issues/465 # https://bitbucket.org/ned/coveragepy/issues/526/generated-xml-invalid-paths-for-cobertura self.make_file("src/mod.py", "print(17)") cov = coverage.Coverage(source=["src"]) self.start_import_stop(cov, "mod", modfile="src/mod.py") self.assert_package_and_class_tags(cov, """\ """) xml = self.stdout() self.assert_source(xml, "src") def clean(text, scrub=None): """Clean text to prepare it for comparison. Remove text matching `scrub`, and leading whitespace. Convert backslashes to forward slashes. """ if scrub: text = re.sub(scrub, "", text) text = re.sub(r"(?m)^\s+", "", text) text = re.sub(r"\\", "/", text) return text class XmlGoldTest(CoverageGoldTest): """Tests of XML reporting that use gold files.""" # TODO: this should move out of html. root_dir = 'tests/farm/html' def test_a_xml_1(self): self.output_dir("out/xml_1") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage() cov.start() import a # pragma: nested cov.stop() # pragma: nested cov.xml_report(a, outfile="../out/xml_1/coverage.xml") source_path = coverage.files.relative_directory().rstrip(r"\/") compare("gold_x_xml", "out/xml_1", scrubs=[ (r' timestamp="\d+"', ' timestamp="TIMESTAMP"'), (r' version="[-.\w]+"', ' version="VERSION"'), (r'\s*.*?\s*', '%s' % source_path), (r'/coverage.readthedocs.io/?[-.\w/]*', '/coverage.readthedocs.io/VER'), ]) def test_a_xml_2(self): self.output_dir("out/xml_2") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage(config_file="run_a_xml_2.ini") cov.start() import a # pragma: nested cov.stop() # pragma: nested cov.xml_report(a) source_path = coverage.files.relative_directory().rstrip(r"\/") compare("gold_x_xml", "out/xml_2", scrubs=[ (r' timestamp="\d+"', ' timestamp="TIMESTAMP"'), (r' version="[-.\w]+"', ' version="VERSION"'), (r'\s*.*?\s*', '%s' % source_path), (r'/coverage.readthedocs.io/?[-.\w/]*', '/coverage.readthedocs.io/VER'), ]) def test_y_xml_branch(self): self.output_dir("out/y_xml_branch") with change_dir("src"): # pylint: disable=import-error cov = coverage.Coverage(branch=True) cov.start() import y # pragma: nested cov.stop() # pragma: nested cov.xml_report(y, outfile="../out/y_xml_branch/coverage.xml") source_path = coverage.files.relative_directory().rstrip(r"\/") compare("gold_y_xml_branch", "out/y_xml_branch", scrubs=[ (r' timestamp="\d+"', ' timestamp="TIMESTAMP"'), (r' version="[-.\w]+"', ' version="VERSION"'), (r'\s*.*?\s*', '%s' % source_path), (r'/coverage.readthedocs.io/?[-.\w/]*', '/coverage.readthedocs.io/VER'), ]) python-coverage-4.5+dfsg.1.orig/tests/helpers.py0000644000076600000620000000766013173515667017663 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Helpers for coverage.py tests.""" import glob import itertools import os import re import shutil import subprocess import sys from unittest_mixins import ModuleCleaner from coverage import env from coverage.backward import invalidate_import_caches, unicode_class from coverage.misc import output_encoding def run_command(cmd): """Run a command in a sub-process. Returns the exit status code and the combined stdout and stderr. """ if env.PY2 and isinstance(cmd, unicode_class): cmd = cmd.encode(sys.getfilesystemencoding()) # In some strange cases (PyPy3 in a virtualenv!?) the stdout encoding of # the subprocess is set incorrectly to ascii. Use an environment variable # to force the encoding to be the same as ours. sub_env = dict(os.environ) sub_env['PYTHONIOENCODING'] = output_encoding() proc = subprocess.Popen( cmd, shell=True, env=sub_env, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) output, _ = proc.communicate() status = proc.returncode # Get the output, and canonicalize it to strings with newlines. if not isinstance(output, str): output = output.decode(output_encoding()) output = output.replace('\r', '') return status, output class CheckUniqueFilenames(object): """Asserts the uniqueness of file names passed to a function.""" def __init__(self, wrapped): self.filenames = set() self.wrapped = wrapped @classmethod def hook(cls, obj, method_name): """Replace a method with our checking wrapper. The method must take a string as a first argument. That argument will be checked for uniqueness across all the calls to this method. The values don't have to be file names actually, just strings, but we only use it for filename arguments. """ method = getattr(obj, method_name) hook = cls(method) setattr(obj, method_name, hook.wrapper) return hook def wrapper(self, filename, *args, **kwargs): """The replacement method. Check that we don't have dupes.""" assert filename not in self.filenames, ( "File name %r passed to %r twice" % (filename, self.wrapped) ) self.filenames.add(filename) ret = self.wrapped(filename, *args, **kwargs) return ret def re_lines(text, pat, match=True): """Return the text of lines that match `pat` in the string `text`. If `match` is false, the selection is inverted: only the non-matching lines are included. Returns a string, the text of only the selected lines. """ return "".join(l for l in text.splitlines(True) if bool(re.search(pat, l)) == match) def re_line(text, pat): """Return the one line in `text` that matches regex `pat`. Raises an AssertionError if more than one, or less than one, line matches. """ lines = re_lines(text, pat).splitlines() assert len(lines) == 1 return lines[0] class SuperModuleCleaner(ModuleCleaner): """Remember the state of sys.modules and restore it later.""" def clean_local_file_imports(self): """Clean up the results of calls to `import_local_file`. Use this if you need to `import_local_file` the same file twice in one test. """ # So that we can re-import files, clean them out first. self.cleanup_modules() # Also have to clean out the .pyc file, since the timestamp # resolution is only one second, a changed file might not be # picked up. for pyc in itertools.chain(glob.glob('*.pyc'), glob.glob('*$py.class')): os.remove(pyc) if os.path.exists("__pycache__"): shutil.rmtree("__pycache__") invalidate_import_caches() python-coverage-4.5+dfsg.1.orig/tests/test_pickle2json.py0000644000076600000620000000344213146037571021466 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests for coverage.pickle2json""" from coverage.backward import pickle, iitems from coverage.data import CoverageData from coverage.pickle2json import pickle2json from tests.coveragetest import CoverageTest from tests.test_data import DataTestHelpers, LINES_1, ARCS_3 class Pickle2JsonTestInTempDir(DataTestHelpers, CoverageTest): """Tests pickle2json.py.""" no_files_in_temp_dir = True def write_pickled_file(self, covdata, filename): """Write coverage data as pickled `filename`.""" # Create the file data. file_data = {} if covdata._arcs: file_data['arcs'] = dict((f, list(amap)) for f, amap in iitems(covdata._arcs)) else: file_data['lines'] = dict((f, list(lmap)) for f, lmap in iitems(covdata._lines)) # Write the pickle to the file. with open(filename, 'wb') as file_obj: pickle.dump(file_data, file_obj, 2) def test_read_write_lines_pickle(self): # Test the old pickle format. covdata1 = CoverageData() covdata1.add_lines(LINES_1) self.write_pickled_file(covdata1, "lines.pkl") pickle2json("lines.pkl", "lines.json") covdata2 = CoverageData() covdata2.read_file("lines.json") self.assert_lines1_data(covdata2) def test_read_write_arcs_pickle(self): # Test the old pickle format. covdata1 = CoverageData() covdata1.add_arcs(ARCS_3) self.write_pickled_file(covdata1, "arcs.pkl") pickle2json("arcs.pkl", "arcs.json") covdata2 = CoverageData() covdata2.read_file("arcs.json") self.assert_arcs3_data(covdata2) python-coverage-4.5+dfsg.1.orig/tests/test_testing.py0000644000076600000620000002370213173515667020730 0ustar staff# -*- coding: utf-8 -*- # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests that our test infrastructure is really working!""" import datetime import os import sys import pytest import coverage from coverage.backunittest import TestCase, unittest from coverage.files import actual_path from coverage.misc import StopEverything from tests.coveragetest import CoverageTest, convert_skip_exceptions from tests.helpers import CheckUniqueFilenames, re_lines, re_line class TestingTest(TestCase): """Tests of helper methods on `backunittest.TestCase`.""" def test_assert_count_equal(self): self.assertCountEqual(set(), set()) self.assertCountEqual(set([1,2,3]), set([3,1,2])) with self.assertRaises(AssertionError): self.assertCountEqual(set([1,2,3]), set()) with self.assertRaises(AssertionError): self.assertCountEqual(set([1,2,3]), set([4,5,6])) class CoverageTestTest(CoverageTest): """Test the methods in `CoverageTest`.""" def test_arcz_to_arcs(self): self.assertEqual(self.arcz_to_arcs(".1 12 2."), [(-1, 1), (1, 2), (2, -1)]) self.assertEqual(self.arcz_to_arcs("-11 12 2-5"), [(-1, 1), (1, 2), (2, -5)]) self.assertEqual( self.arcz_to_arcs("-QA CB IT Z-A"), [(-26, 10), (12, 11), (18, 29), (35, -10)] ) def test_file_exists(self): self.make_file("whoville.txt", "We are here!") self.assert_exists("whoville.txt") self.assert_doesnt_exist("shadow.txt") with self.assertRaises(AssertionError): self.assert_doesnt_exist("whoville.txt") with self.assertRaises(AssertionError): self.assert_exists("shadow.txt") def test_assert_startwith(self): self.assert_starts_with("xyzzy", "xy") self.assert_starts_with("xyz\nabc", "xy") self.assert_starts_with("xyzzy", ("x", "z")) with self.assertRaises(AssertionError): self.assert_starts_with("xyz", "a") with self.assertRaises(AssertionError): self.assert_starts_with("xyz\nabc", "a") def test_assert_recent_datetime(self): def now_delta(seconds): """Make a datetime `seconds` seconds from now.""" return datetime.datetime.now() + datetime.timedelta(seconds=seconds) # Default delta is 10 seconds. self.assert_recent_datetime(now_delta(0)) self.assert_recent_datetime(now_delta(-9)) with self.assertRaises(AssertionError): self.assert_recent_datetime(now_delta(-11)) with self.assertRaises(AssertionError): self.assert_recent_datetime(now_delta(1)) # Delta is settable. self.assert_recent_datetime(now_delta(0), seconds=120) self.assert_recent_datetime(now_delta(-100), seconds=120) with self.assertRaises(AssertionError): self.assert_recent_datetime(now_delta(-1000), seconds=120) with self.assertRaises(AssertionError): self.assert_recent_datetime(now_delta(1), seconds=120) def test_assert_warnings(self): cov = coverage.Coverage() # Make a warning, it should catch it properly. with self.assert_warnings(cov, ["Hello there!"]): cov._warn("Hello there!") # The expected warnings are regexes. with self.assert_warnings(cov, ["Hello.*!"]): cov._warn("Hello there!") # There can be a bunch of actual warnings. with self.assert_warnings(cov, ["Hello.*!"]): cov._warn("You there?") cov._warn("Hello there!") # There can be a bunch of expected warnings. with self.assert_warnings(cov, ["Hello.*!", "You"]): cov._warn("You there?") cov._warn("Hello there!") # But if there are a bunch of expected warnings, they have to all happen. warn_regex = r"Didn't find warning 'You' in \['Hello there!'\]" with self.assertRaisesRegex(AssertionError, warn_regex): with self.assert_warnings(cov, ["Hello.*!", "You"]): cov._warn("Hello there!") # Make a different warning than expected, it should raise an assertion. warn_regex = r"Didn't find warning 'Not me' in \['Hello there!'\]" with self.assertRaisesRegex(AssertionError, warn_regex): with self.assert_warnings(cov, ["Not me"]): cov._warn("Hello there!") # Try checking a warning that shouldn't appear: happy case. with self.assert_warnings(cov, ["Hi"], not_warnings=["Bye"]): cov._warn("Hi") # But it should fail if the unexpected warning does appear. warn_regex = r"Found warning 'Bye' in \['Hi', 'Bye'\]" with self.assertRaisesRegex(AssertionError, warn_regex): with self.assert_warnings(cov, ["Hi"], not_warnings=["Bye"]): cov._warn("Hi") cov._warn("Bye") # assert_warnings shouldn't hide a real exception. with self.assertRaises(ZeroDivisionError): with self.assert_warnings(cov, ["Hello there!"]): raise ZeroDivisionError("oops") def test_assert_no_warnings(self): cov = coverage.Coverage() # Happy path: no warnings. with self.assert_warnings(cov, []): pass # If you said there would be no warnings, and there were, fail! warn_regex = r"Unexpected warnings: \['Watch out!'\]" with self.assertRaisesRegex(AssertionError, warn_regex): with self.assert_warnings(cov, []): cov._warn("Watch out!") def test_sub_python_is_this_python(self): # Try it with a Python command. self.set_environ('COV_FOOBAR', 'XYZZY') self.make_file("showme.py", """\ import os, sys print(sys.executable) print(os.__file__) print(os.environ['COV_FOOBAR']) """) out = self.run_command("python showme.py").splitlines() self.assertEqual(actual_path(out[0]), actual_path(sys.executable)) self.assertEqual(out[1], os.__file__) self.assertEqual(out[2], 'XYZZY') # Try it with a "coverage debug sys" command. out = self.run_command("coverage debug sys") executable = re_line(out, "executable:") executable = executable.split(":", 1)[1].strip() self.assertTrue(_same_python_executable(executable, sys.executable)) # "environment: COV_FOOBAR = XYZZY" or "COV_FOOBAR = XYZZY" environ = re_line(out, "COV_FOOBAR") _, _, environ = environ.rpartition(":") self.assertEqual(environ.strip(), "COV_FOOBAR = XYZZY") class CheckUniqueFilenamesTest(CoverageTest): """Tests of CheckUniqueFilenames.""" run_in_temp_dir = False class Stub(object): """A stand-in for the class we're checking.""" def __init__(self, x): self.x = x def method(self, filename, a=17, b="hello"): """The method we'll wrap, with args to be sure args work.""" return (self.x, filename, a, b) def test_detect_duplicate(self): stub = self.Stub(23) CheckUniqueFilenames.hook(stub, "method") # Two method calls with different names are fine. assert stub.method("file1") == (23, "file1", 17, "hello") assert stub.method("file2", 1723, b="what") == (23, "file2", 1723, "what") # A duplicate file name trips an assertion. with self.assertRaises(AssertionError): stub.method("file1") @pytest.mark.parametrize("text, pat, result", [ ("line1\nline2\nline3\n", "line", "line1\nline2\nline3\n"), ("line1\nline2\nline3\n", "[13]", "line1\nline3\n"), ("line1\nline2\nline3\n", "X", ""), ]) def test_re_lines(text, pat, result): assert re_lines(text, pat) == result @pytest.mark.parametrize("text, pat, result", [ ("line1\nline2\nline3\n", "line", ""), ("line1\nline2\nline3\n", "[13]", "line2\n"), ("line1\nline2\nline3\n", "X", "line1\nline2\nline3\n"), ]) def test_re_lines_inverted(text, pat, result): assert re_lines(text, pat, match=False) == result @pytest.mark.parametrize("text, pat, result", [ ("line1\nline2\nline3\n", "2", "line2"), ]) def test_re_line(text, pat, result): assert re_line(text, pat) == result @pytest.mark.parametrize("text, pat", [ ("line1\nline2\nline3\n", "line"), # too many matches ("line1\nline2\nline3\n", "X"), # no matches ]) def test_re_line_bad(text, pat): with pytest.raises(AssertionError): re_line(text, pat) def test_convert_skip_exceptions(): @convert_skip_exceptions def some_method(ret=None, exc=None): """Be like a test case.""" if exc: raise exc("yikes!") return ret # Normal flow is normal. assert some_method(ret=[17, 23]) == [17, 23] # Exceptions are raised normally. with pytest.raises(ValueError): some_method(exc=ValueError) # But a StopEverything becomes a SkipTest. with pytest.raises(unittest.SkipTest): some_method(exc=StopEverything) def _same_python_executable(e1, e2): """Determine if `e1` and `e2` refer to the same Python executable. Either path could include symbolic links. The two paths might not refer to the exact same file, but if they are in the same directory and their numeric suffixes aren't different, they are the same executable. """ e1 = os.path.abspath(os.path.realpath(e1)) e2 = os.path.abspath(os.path.realpath(e2)) if os.path.dirname(e1) != os.path.dirname(e2): return False # pragma: only failure e1 = os.path.basename(e1) e2 = os.path.basename(e2) if e1 == "python" or e2 == "python" or e1 == e2: # Python and Python2.3: OK # Python2.3 and Python: OK # Python and Python: OK # Python2.3 and Python2.3: OK return True return False # pragma: only failure python-coverage-4.5+dfsg.1.orig/tests/goldtest.py0000644000076600000620000000254413146037571020033 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """A test base class for tests based on gold file comparison.""" import os import sys from unittest_mixins import change_dir # pylint: disable=unused-import from tests.coveragetest import CoverageTest from tests.test_farm import clean # Import helpers, eventually test_farm.py will go away. from tests.test_farm import ( # pylint: disable=unused-import compare, contains, doesnt_contain, contains_any, ) class CoverageGoldTest(CoverageTest): """A test based on gold files.""" run_in_temp_dir = False def setUp(self): super(CoverageGoldTest, self).setUp() self.chdir(self.root_dir) # Modules should be importable from the current directory. sys.path.insert(0, '') def output_dir(self, the_dir): """Declare where the output directory is. The output directory is deleted at the end of the test, unless the COVERAGE_KEEP_OUTPUT environment variable is set. """ # To make sure tests are isolated, we always clean the directory at the # beginning of the test. clean(the_dir) if not os.environ.get("COVERAGE_KEEP_OUTPUT"): # pragma: part covered self.addCleanup(clean, the_dir) python-coverage-4.5+dfsg.1.orig/tests/test_collector.py0000644000076600000620000000307513146037571021233 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Tests of coverage/collector.py and other collectors.""" import os.path import coverage from tests.coveragetest import CoverageTest from tests.helpers import CheckUniqueFilenames class CollectorTest(CoverageTest): """Test specific aspects of the collection process.""" def test_should_trace_cache(self): # The tracers should only invoke should_trace once for each file name. # Make some files that invoke each other. self.make_file("f1.py", """\ def f1(x, f): return f(x) """) self.make_file("f2.py", """\ import f1 def func(x): return f1.f1(x, otherfunc) def otherfunc(x): return x*x for i in range(10): func(i) """) # Trace one file, but not the other. CheckUniqueFilenames will assert # that _should_trace hasn't been called twice for the same file. cov = coverage.Coverage(include=["f1.py"]) should_trace_hook = CheckUniqueFilenames.hook(cov, '_should_trace') # Import the Python file, executing it. self.start_import_stop(cov, "f2") # Double-check that our files were checked. abs_files = set(os.path.abspath(f) for f in should_trace_hook.filenames) self.assertIn(os.path.abspath("f1.py"), abs_files) self.assertIn(os.path.abspath("f2.py"), abs_files) python-coverage-4.5+dfsg.1.orig/TODO.txt0000644000076600000620000002251413146037571015777 0ustar staffCoverage.py TODO Key: * Heading - Not done yet. + Done. x Not going to do. * 4.0 - What defaults should change? x --source = . ? x --branch = True ? - Remove 2.3, 2.4, 2.5 limitations + set, sorted, reversed, rpartition + generator expressions + decorators + collections.defaultdict + .startswith((,)) + "with" statements - .format() ? + try/except/finally + with assertRaises + addCleanup instead of tearDown + exec statement can look like a function in py2 (since when?) - runpy ? + we can use "except ExcClass as e:" - Plugins + Clean up + implement plugin support in CTracer + remove plugin support from PyTracer x add services: - filelocator - warning - dynamic_source_filename: return should be a canonical path - update the omit test to use "quux*" instead of "*quux*" + docs + Make reports use filenames, not module names - documentation - test helpers + cov.config["run:branch"] api (well, coverage.get_option etc) + "added in 4.0" - tweaks to theme? - Plugins! Once per process Once per file - create a file tracer - call its has_dynamic_source_file() Once per call Once per line - build process - don't publish to nedbat.com any more (but still need the sample html reports) + don't need .px tooling - write a new nedbat.com/code/coverage page. - all doc links should point to rtfd + Remove code only run on <2.6 + Change data file to json + Create data api + gevent, etc. + Remove the old command-line syntax + A pain, b/c of the structure of the tests. + BTW: make an easier way to write those tests. - tests - test the kit has the right contents - test the kit installs the right stuff * --source stuff: + warn if a package is never found. + warn if no data was collected - tie --source into reporting * Soon + Better omit handling that ignores files during measurement. - Deal with ~ in specified paths correctly. + while TRUE claims to be partial. + A way to mark lines as partial branches, with a regex? + Default to "while True:", "while 1:" + HTML keyboard short cuts * 3.2 + Some kind of indication in the HTML where yellow lines aren't going. - Profile the reporting code: it's REALLY slow. - parser is doing some redundant work. + Analysis class should do rolling up of stats also (actually Numbers) + Update docs for --branch. x self.coverage.data.has_arcs is ugly. + Branches that never jump to nocover lines shouldn't be marked as partial. (see top of test_cogapp for examples) + Maybe turning off yellow lines should make those lines green? + A missing branch to leave the function shows an annotation of -1. Now "exit". + XML report needs to get branch information. + Add branch info to "coverage debug data" + Polish up the help, and double-check the docs. * Speed + C extension collector - bitvector in trace extension. - Ignore certain modules + Record linenos rather than (file,lineno) pairs in tracer. x Tricky swapping of collector like figleaf, pycov, et al. (Don't need to do this with C collector). - Seems like there should be a faster way to manage all the line number sets in CodeParser.raw_parse. - If tracing, canonical_filename_cache overlaps with should_trace_cache. Skip canonical_filename_cache. Maybe it isn't even worth it... - Would pre-allocating line number integers make the C tracer faster? It would use less memory. * Accuracy - Record magic number of module to ensure code hasn't changed - Record version of coverage data file, so we can update what's stored there. - Record options in coverage data file, so multiple runs are certain to make sense together. - Do I still need the lines in annotate_file that deal specially with "else"? * Power + Branch coverage Titus' idea: 1: if a: 2: b = 2 3: c = 3 if the coverage data shows 1,2,3, it was if-then. if it's 1,3, then the missing else was executed. + API for getting coverage data. - Instruction tracing instead of line tracing. - Path tracing (how does this even work?) - Count execution of lines - Track callers of functions (ala std module trace) - Method/Class/Module coverage reporting. - .coverage files that can be kept separate, rather than accumulated. - test/coverage map: http://rbtcollins.wordpress.com/2009/09/16/back-from-hiatus/ - Similar to figleaf's sections. * Convenience - Command line modules should also be directories, meaning all the modules in that directory. - Why can't a morf also be a string, the name of a module? - ignore by module as well as file? + Use a .coveragerc file to control coverage.py without the programmatic API. - Add a --data switch to explicitly control the data file on the command line. x Why can't you specify execute (-x) and report (-r) in the same invocation? Maybe just because -x needs the rest of the command line? + Support 2.3 - 3.1! http://pythonology.blogspot.com/2009/02/making-code-run-on-python-20-through-30.html http://www.rfk.id.au/blog/entry/preparing-pyenchant-for-python-3 http://pydev.blogspot.com/2008/11/making-code-work-in-python-2-and-3.html + Explicitly set pickle protocol to 2. - An inference mode that marks lines as executed if they "must have been" executed: class definitions, etc, when coverage is started after the class is defined. - Different categories of exclude pragma? So you can enable and disable them from the command line, to reconsider exclusions. + Reporting on files never touched by coverage.py (package completeness) - A setup.py command? http://jeetworks.org/node/50 - Deltas: indicate the change in coverage percentage from the last run. + Show lines missing rather than lines run in the reporting, since that's what you need to focus on. * Beauty + HTML report - Colored bars indicating coverage per file. - Package navigation. - Rolled-up statistics. - Some way to focus in on red and yellow - Show only lines near highlights? + Jump to next highlight? + Keyboard navigation: j and k. - Cookie for changes to pyfile.html state. + Clickable column headers on the index page. + Syntax coloring in HTML report. + Dynamic effects in HTML report. + Footer in reports pointing to coverage.py home page. + Baseline grid for linenumber font. + Separate out css and HTML. + Does it work right with utf-8 source files? http://www.python.org/dev/peps/pep-0263/ - Use vim modeline to determine tab width: http://vimdoc.sourceforge.net/htmldoc/options.html#modeline * Community + New docs, rather than pointing to Gareth's + Min python version is 2.3. - Three phases of work: - Collection - Analysis - Reporting - Distinction between: - ignore (files not to collect) - exclude (lines not to report as missed) - omit (files not to report) - Changes from coverage.py 2.x: - Bare "except:" lines now count as executable code. - Double function decorators: all decorator lines count as executable code. x Document the .coverage file format. + HTML reporting. - Much more detail about what's in the report. - References between pages are off: - They have tags around them. - They use #anchors that don't survive the px->html conversion. + Be sure --help text is complete (-i is missing). + Host the project somewhere with a real bug tracker: bitbucket.org + Point discussion to TIP - PEP 8 compliance? * Programmability + Don't use sys.exit in CoverageScript. + Remove singleton + Initialization of instance variables in the class. * Installation x How will coverage.py package install over coverage.py module? x pip can't install it: it reads the coverage.py html page, and finds the kit link, but then can't handle the root-relative link. * Modernization + Decide on minimum supported version + 2.3 + Get rid of the basestring protection + Use enumerate + Use sets instead of dicts + Switch from getopt to optparse. + Get rid of the recursive nonsense. + Docstrings. + Remove huge document-style comments. - Better names: + self.cache -> self.cache_filename -> CoverageData.filename + self.usecache -> CoverageData.use_file - More classes: - Module munging + Coverage data files + Why are some imports at the top of the file, and some in functions? + Get rid of sys.exitfunc use. + True and False (with no backward adaptation: the constants are new in 2.2.1) + Get rid of compiler module + In analyzing code + In test_coverage.py + Style: + lineno + filename * Correctness - What does -p (parallel mode) mean with -e (erase data)? * Tests + Switch to a real test runner, like nose. + Test both the C trace function and the Python trace function. + parser.py has no direct tests. + Tests about the .coverage file. + Tests about the --long-form of arguments. + Tests about overriding the .coverage filename. - Tests about parallel mode. + Tests about assigning a multi-line string. - Tests about tricky docstrings. + Coverage test coverage.py! - Tests that tracing stops after calling stop() - More intensive thread testing. x Tests about the "import __main__" in cmdline.py + What happens if the -x script raises an exception? - Test that the kit has all the proper contents. python-coverage-4.5+dfsg.1.orig/PKG-INFO0000644000076600000620000001450113235414516015560 0ustar staffMetadata-Version: 1.2 Name: coverage Version: 4.5 Summary: Code coverage measurement for Python Home-page: https://bitbucket.org/ned/coveragepy Author: Ned Batchelder and 100 others Author-email: ned@nedbatchelder.com License: Apache 2.0 Description-Content-Type: UNKNOWN Description: .. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt =========== Coverage.py =========== Code coverage testing for Python. | |license| |versions| |status| |docs| | |ci-status| |win-ci-status| |codecov| | |kit| |format| |saythanks| .. downloads badge seems to be broken... |downloads| Coverage.py measures code coverage, typically during test execution. It uses the code analysis tools and tracing hooks provided in the Python standard library to determine which lines are executable, and which have been executed. Coverage.py runs on many versions of Python: * CPython 2.6, 2.7 and 3.3 through 3.7. * PyPy2 5.10 and PyPy3 5.10. * Jython 2.7.1, though not for reporting. * IronPython 2.7.7, though not for reporting. Documentation is on `Read the Docs`_. Code repository and issue tracker are on `Bitbucket`_, with a mirrored repository on `GitHub`_. .. _Read the Docs: https://coverage.readthedocs.io/ .. _Bitbucket: https://bitbucket.org/ned/coveragepy .. _GitHub: https://github.com/nedbat/coveragepy **New in 4.5:** Configurator plug-ins. New in 4.4: Suppressable warnings, continuous coverage measurement. New in 4.3: HTML ``--skip-covered``, sys.excepthook support, tox.ini support. New in 4.2: better support for multiprocessing and combining data. New in 4.1: much-improved branch coverage. New in 4.0: ``--concurrency``, plugins for non-Python files, setup.cfg support, --skip-covered, HTML filtering, and more than 50 issues closed. Getting Started --------------- See the `Quick Start section`_ of the docs. .. _Quick Start section: https://coverage.readthedocs.io/#quick-start Contributing ------------ See the `Contributing section`_ of the docs. .. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html License ------- Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_. .. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 .. _NOTICE.txt: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. |ci-status| image:: https://travis-ci.org/nedbat/coveragepy.svg?branch=master :target: https://travis-ci.org/nedbat/coveragepy :alt: Build status .. |win-ci-status| image:: https://ci.appveyor.com/api/projects/status/kmeqpdje7h9r6vsf/branch/master?svg=true :target: https://ci.appveyor.com/project/nedbat/coveragepy :alt: Windows build status .. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat :target: https://coverage.readthedocs.io/ :alt: Documentation .. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master :alt: Requirements status .. |kit| image:: https://badge.fury.io/py/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: PyPI status .. |format| image:: https://img.shields.io/pypi/format/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Kit format .. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Weekly PyPI downloads .. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Python versions supported .. |status| image:: https://img.shields.io/pypi/status/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Package stability .. |license| image:: https://img.shields.io/pypi/l/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: License .. |codecov| image:: http://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2 :target: http://codecov.io/github/nedbat/coveragepy?branch=master :alt: Coverage! .. |saythanks| image:: https://img.shields.io/badge/saythanks.io-%E2%98%BC-1EAEDB.svg :target: https://saythanks.io/to/nedbat :alt: Say thanks :) Keywords: code coverage testing Platform: UNKNOWN Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Programming Language :: Python :: Implementation :: Jython Classifier: Programming Language :: Python :: Implementation :: IronPython Classifier: Topic :: Software Development :: Quality Assurance Classifier: Topic :: Software Development :: Testing Classifier: Development Status :: 5 - Production/Stable Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4 python-coverage-4.5+dfsg.1.orig/MANIFEST.in0000644000076600000620000000213013146037571016217 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # MANIFEST.in file for coverage.py include CONTRIBUTORS.txt include CHANGES.rst include LICENSE.txt include MANIFEST.in include Makefile include NOTICE.txt include README.rst include TODO.txt include __main__.py include .travis.yml include appveyor.yml include circle.yml include howto.txt include igor.py include metacov.ini include pylintrc include setup.py include tox.ini include tox_wheels.ini recursive-include ci *.* exclude ci/appveyor.token recursive-include coverage/fullcoverage *.py recursive-include coverage/ctracer *.c *.h recursive-include doc conf.py *.pip *.rst *.txt recursive-include doc/_static *.* prune doc/_build recursive-include requirements *.pip recursive-include tests *.py *.tok recursive-include tests/farm */gold*/*.* */gold*/*/*.* recursive-include tests/farm/*/src * *.* recursive-include tests js/*.* qunit/*.* prune tests/eggsrc/build prune tests/eggsrc/dist prune tests/eggsrc/*.egg-info global-exclude *.py[co] python-coverage-4.5+dfsg.1.orig/tox.ini0000644000076600000620000000613113234613364015777 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt [tox] envlist = py{26,27,33,34,35,36,37}, pypy{2,3}, jython, doc, lint skip_missing_interpreters = {env:COVERAGE_SKIP_MISSING_INTERPRETERS:True} toxworkdir = {env:TOXWORKDIR:.tox} [testenv] usedevelop = True deps = # https://requires.io/github/nedbat/coveragepy/requirements/ -rrequirements/pytest.pip pip==9.0.1 # setuptools>=36 vendors packages which pollute the coverage output in tests setuptools==35.0.2 mock==2.0.0 PyContracts==1.8.0 unittest-mixins==1.4 #-e/Users/ned/unittest_mixins py26: unittest2==1.1.0 py{27,33,34,35,36}: gevent==1.2.2 py26: eventlet==0.21.0 py{27,33,34,35,36,37}: eventlet==0.22.0 py{26,27,33,34,35,36,37}: greenlet==0.4.13 # Windows can't update the pip version with pip running, so use Python # to install things. install_command = python -m pip install -U {opts} {packages} passenv = * setenv = pypy,pypy{2,3}: COVERAGE_NO_CTRACER=no C extension under PyPy jython: COVERAGE_NO_CTRACER=no C extension under Jython jython: PYTEST_ADDOPTS=-n 0 commands = python setup.py --quiet clean develop # Create tests/zipmods.zip # Install the egg1 egg # Remove the C extension so that we can test the PyTracer python igor.py zip_mods install_egg remove_extension # When running parallel tests, many processes might all try to import the # same modules at once. This should be safe, but especially on Python 3.3, # this caused a number of test failures trying to import usepkgs. To # prevent the race condition, pre-compile the tests/modules directory. py33: python -m compileall -q -f tests/modules py33: python -c "import time; time.sleep(1.1)" # Test with the PyTracer python igor.py test_with_tracer py {posargs} # Build the C extension and test with the CTracer python setup.py --quiet build_ext --inplace python igor.py test_with_tracer c {posargs} [testenv:py26] install_command = python -m pip.__main__ install -U {opts} {packages} [testenv:pypy] # The "pypy" environment is for Travis. Probably can make Travis use one of # the other environments... basepython = pypy [testenv:pypy2] basepython = pypy2 [testenv:pypy3] basepython = pypy3 [testenv:jython] basepython = jython [testenv:doc] # Build the docs so we know if they are successful. We build twice: once with # -q to get all warnings, and once with -QW to get a success/fail status # return. deps = -rdoc/requirements.pip commands = doc8 -q --ignore-path doc/_build doc CHANGES.rst README.rst sphinx-build -b html -aqE doc doc/_build/html rst2html.py --strict README.rst doc/_build/trash sphinx-build -b html -b linkcheck -aEnq doc doc/_build/html sphinx-build -b html -b linkcheck -aEnQW doc doc/_build/html [testenv:lint] deps = -rrequirements/dev.pip setenv = LINTABLE = coverage tests igor.py setup.py __main__.py commands = python -m pylint --notes= {env:LINTABLE} python -m tabnanny {env:LINTABLE} python igor.py check_eol python-coverage-4.5+dfsg.1.orig/NOTICE.txt0000644000076600000620000000125113235412433016177 0ustar staffCopyright 2001 Gareth Rees. All rights reserved. Copyright 2004-2018 Ned Batchelder. All rights reserved. Except where noted otherwise, this software is licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. python-coverage-4.5+dfsg.1.orig/setup.py0000644000076600000620000001523113226756435016207 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Code coverage measurement for Python""" # Distutils setup for coverage.py # This file is used unchanged under all versions of Python, 2.x and 3.x. import os import sys from setuptools import setup from distutils.core import Extension # pylint: disable=no-name-in-module, import-error from distutils.command.build_ext import build_ext # pylint: disable=no-name-in-module, import-error from distutils import errors # pylint: disable=no-name-in-module # Get or massage our metadata. We exec coverage/version.py so we can avoid # importing the product code into setup.py. classifiers = """\ Environment :: Console Intended Audience :: Developers License :: OSI Approved :: Apache Software License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.6 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.3 Programming Language :: Python :: 3.4 Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: PyPy Programming Language :: Python :: Implementation :: Jython Programming Language :: Python :: Implementation :: IronPython Topic :: Software Development :: Quality Assurance Topic :: Software Development :: Testing """ cov_ver_py = os.path.join(os.path.split(__file__)[0], "coverage/version.py") with open(cov_ver_py) as version_file: # __doc__ will be overwritten by version.py. doc = __doc__ # Keep pylint happy. __version__ = __url__ = version_info = "" # Execute the code in version.py. exec(compile(version_file.read(), cov_ver_py, 'exec')) with open("README.rst") as readme: long_description = readme.read().replace("https://coverage.readthedocs.io", __url__) with open("CONTRIBUTORS.txt", "rb") as contributors: paras = contributors.read().split(b"\n\n") num_others = len(paras[-1].splitlines()) num_others += 1 # Count Gareth Rees, who is mentioned in the top paragraph. classifier_list = classifiers.splitlines() if version_info[3] == 'alpha': devstat = "3 - Alpha" elif version_info[3] in ['beta', 'candidate']: devstat = "4 - Beta" else: assert version_info[3] == 'final' devstat = "5 - Production/Stable" classifier_list.append("Development Status :: " + devstat) # Create the keyword arguments for setup() setup_args = dict( name='coverage', version=__version__, packages=[ 'coverage', ], package_data={ 'coverage': [ 'htmlfiles/*.*', 'fullcoverage/*.*', ] }, entry_points={ # Install a script as "coverage", and as "coverage[23]", and as # "coverage-2.7" (or whatever). 'console_scripts': [ 'coverage = coverage.cmdline:main', 'coverage%d = coverage.cmdline:main' % sys.version_info[:1], 'coverage-%d.%d = coverage.cmdline:main' % sys.version_info[:2], ], }, # We need to get HTML assets from our htmlfiles directory. zip_safe=False, author='Ned Batchelder and {0} others'.format(num_others), author_email='ned@nedbatchelder.com', description=doc, long_description=long_description, keywords='code coverage testing', license='Apache 2.0', classifiers=classifier_list, url="https://bitbucket.org/ned/coveragepy", python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4", ) # A replacement for the build_ext command which raises a single exception # if the build fails, so we can fallback nicely. ext_errors = ( errors.CCompilerError, errors.DistutilsExecError, errors.DistutilsPlatformError, ) if sys.platform == 'win32': # distutils.msvc9compiler can raise an IOError when failing to # find the compiler ext_errors += (IOError,) class BuildFailed(Exception): """Raise this to indicate the C extension wouldn't build.""" def __init__(self): Exception.__init__(self) self.cause = sys.exc_info()[1] # work around py 2/3 different syntax class ve_build_ext(build_ext): """Build C extensions, but fail with a straightforward exception.""" def run(self): """Wrap `run` with `BuildFailed`.""" try: build_ext.run(self) except errors.DistutilsPlatformError: raise BuildFailed() def build_extension(self, ext): """Wrap `build_extension` with `BuildFailed`.""" try: # Uncomment to test compile failure handling: # raise errors.CCompilerError("OOPS") build_ext.build_extension(self, ext) except ext_errors: raise BuildFailed() except ValueError as err: # this can happen on Windows 64 bit, see Python issue 7511 if "'path'" in str(err): # works with both py 2/3 raise BuildFailed() raise # There are a few reasons we might not be able to compile the C extension. # Figure out if we should attempt the C extension or not. compile_extension = True if sys.platform.startswith('java'): # Jython can't compile C extensions compile_extension = False if '__pypy__' in sys.builtin_module_names: # Pypy can't compile C extensions compile_extension = False if compile_extension: setup_args.update(dict( ext_modules=[ Extension( "coverage.tracer", sources=[ "coverage/ctracer/datastack.c", "coverage/ctracer/filedisp.c", "coverage/ctracer/module.c", "coverage/ctracer/tracer.c", ], ), ], cmdclass={ 'build_ext': ve_build_ext, }, )) # Py3.x-specific details. if sys.version_info >= (3, 0): setup_args.update(dict( use_2to3=False, )) def main(): """Actually invoke setup() with the arguments we built above.""" # For a variety of reasons, it might not be possible to install the C # extension. Try it with, and if it fails, try it without. try: setup(**setup_args) except BuildFailed as exc: msg = "Couldn't install with extension module, trying without it..." exc_msg = "%s: %s" % (exc.__class__.__name__, exc.cause) print("**\n** %s\n** %s\n**" % (msg, exc_msg)) del setup_args['ext_modules'] setup(**setup_args) if __name__ == '__main__': main() python-coverage-4.5+dfsg.1.orig/appveyor.yml0000644000076600000620000000751013177624110017053 0ustar staff# Appveyor, continuous integration for Windows # https://ci.appveyor.com/project/nedbat/coveragepy version: '{branch}-{build}' shallow_clone: true environment: CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\ci\\run_with_env.cmd" # Parallel pytest gets tangled up with tests that try to create and destroy # .pth files in the shared virtualenv. Disable parallel tests. PYTEST_ADDOPTS: "-n 0" matrix: - JOB: "2.7 32-bit" TOXENV: "py27" PYTHON: "C:\\Python27.11" PYTHON_VERSION: "2.7.11" PYTHON_ARCH: "32" - JOB: "2.7 64-bit" TOXENV: "py27" PYTHON: "C:\\Python27.11-x64" PYTHON_VERSION: "2.7.11" PYTHON_ARCH: "64" - JOB: "3.4 32-bit" TOXENV: "py34" PYTHON: "C:\\Python34" PYTHON_VERSION: "3.4" PYTHON_ARCH: "32" - JOB: "3.4 64-bit" TOXENV: "py34" PYTHON: "C:\\Python34-x64" PYTHON_VERSION: "3.4" PYTHON_ARCH: "64" - JOB: "3.5 32-bit" TOXENV: "py35" PYTHON: "C:\\Python35" PYTHON_VERSION: "3.5.0" PYTHON_ARCH: "32" - JOB: "3.5 64-bit" TOXENV: "py35" PYTHON: "C:\\Python35-x64" PYTHON_VERSION: "3.5.0" PYTHON_ARCH: "64" - JOB: "3.6 32-bit" TOXENV: "py36" PYTHON: "C:\\Python36" PYTHON_VERSION: "3.6.0" PYTHON_ARCH: "32" - JOB: "3.6 64-bit" TOXENV: "py36" PYTHON: "C:\\Python36-x64" PYTHON_VERSION: "3.6.0" PYTHON_ARCH: "64" # Meta coverage - JOB: "Meta 2.7" TOXENV: "py27" PYTHON: "C:\\Python27" PYTHON_VERSION: "2.7" PYTHON_ARCH: "32" COVERAGE_COVERAGE: "yes" - JOB: "Meta 3.5" TOXENV: "py35" PYTHON: "C:\\Python35" PYTHON_VERSION: "3.5" PYTHON_ARCH: "32" COVERAGE_COVERAGE: "yes" init: - "ECHO %TOXENV%" install: # Install Python (from the official .msi of http://python.org) and pip when # not already installed. - ps: if (-not(Test-Path($env:PYTHON))) { & ci\install.ps1 } # Prepend newly installed Python to the PATH of this build (this cannot be # done from inside the powershell script as it would require to restart # the parent CMD process). - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" # Check that we have the expected version and architecture for Python - "python --version" - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" # Upgrade to the latest version of pip to avoid it displaying warnings # about it being out of date. - "pip install --disable-pip-version-check --user --upgrade pip" # And upgrade virtualenv to get the latest pip inside .tox virtualenvs. - "pip install --disable-pip-version-check --user --upgrade virtualenv" # Install requirements. - "%CMD_IN_ENV% pip install -r requirements/ci.pip" # Make a python3.4.bat file in the current directory so that tox will find it # and python3.4 will mean what we want it to. - "python -c \"import os; open('python{0}.{1}.bat'.format(*os.environ['TOXENV'][2:]), 'w').write('@{0}\\\\python \\x25*\\n'.format(os.environ['PYTHON']))\"" build_script: # If not a metacov job, then build wheels and .exe installers. - if NOT "%COVERAGE_COVERAGE%" == "yes" %CMD_IN_ENV% %PYTHON%\python setup.py bdist_wheel bdist_wininst # Push everything in dist\ as an artifact. - ps: if ( Test-Path 'dist' -PathType Container ) { Get-ChildItem dist\*.* | % { Push-AppveyorArtifact $_.FullName -FileName ('dist\' + $_.Name) } } test_script: - "%CMD_IN_ENV% %PYTHON%\\Scripts\\tox" after_test: - if "%COVERAGE_COVERAGE%" == "yes" 7z a metacov-win-%TOXENV%.zip %APPVEYOR_BUILD_FOLDER%\.metacov* - if "%COVERAGE_COVERAGE%" == "yes" %CMD_IN_ENV% %PYTHON%\python igor.py combine_html - if "%COVERAGE_COVERAGE%" == "yes" %CMD_IN_ENV% pip install codecov - if "%COVERAGE_COVERAGE%" == "yes" %CMD_IN_ENV% codecov -X gcov --file coverage.xml artifacts: - path: "metacov-*.zip" python-coverage-4.5+dfsg.1.orig/coverage.egg-info/0000755000076600000620000000000013235414514017745 5ustar staffpython-coverage-4.5+dfsg.1.orig/coverage.egg-info/entry_points.txt0000644000076600000620000000017313235414513023243 0ustar staff[console_scripts] coverage = coverage.cmdline:main coverage-2.7 = coverage.cmdline:main coverage2 = coverage.cmdline:main python-coverage-4.5+dfsg.1.orig/coverage.egg-info/top_level.txt0000644000076600000620000000001113235414513022466 0ustar staffcoverage python-coverage-4.5+dfsg.1.orig/coverage.egg-info/SOURCES.txt0000644000076600000620000001744013235414514021637 0ustar staff.travis.yml CHANGES.rst CONTRIBUTORS.txt LICENSE.txt MANIFEST.in Makefile NOTICE.txt README.rst TODO.txt __main__.py appveyor.yml circle.yml howto.txt igor.py metacov.ini pylintrc setup.cfg setup.py tox.ini tox_wheels.ini ci/README.txt ci/download_appveyor.py ci/install.ps1 ci/manylinux.sh ci/run_with_env.cmd coverage/__init__.py coverage/__main__.py coverage/annotate.py coverage/backunittest.py coverage/backward.py coverage/bytecode.py coverage/cmdline.py coverage/collector.py coverage/config.py coverage/control.py coverage/data.py coverage/debug.py coverage/env.py coverage/execfile.py coverage/files.py coverage/html.py coverage/misc.py coverage/multiproc.py coverage/parser.py coverage/phystokens.py coverage/pickle2json.py coverage/plugin.py coverage/plugin_support.py coverage/python.py coverage/pytracer.py coverage/report.py coverage/results.py coverage/summary.py coverage/templite.py coverage/version.py coverage/xmlreport.py coverage.egg-info/PKG-INFO coverage.egg-info/SOURCES.txt coverage.egg-info/dependency_links.txt coverage.egg-info/entry_points.txt coverage.egg-info/not-zip-safe coverage.egg-info/top_level.txt coverage/ctracer/datastack.c coverage/ctracer/datastack.h coverage/ctracer/filedisp.c coverage/ctracer/filedisp.h coverage/ctracer/module.c coverage/ctracer/stats.h coverage/ctracer/tracer.c coverage/ctracer/tracer.h coverage/ctracer/util.h coverage/fullcoverage/encodings.py coverage/htmlfiles/coverage_html.js coverage/htmlfiles/index.html coverage/htmlfiles/jquery.ba-throttle-debounce.min.js coverage/htmlfiles/jquery.hotkeys.js coverage/htmlfiles/jquery.isonscreen.js coverage/htmlfiles/jquery.min.js coverage/htmlfiles/jquery.tablesorter.min.js coverage/htmlfiles/keybd_closed.png coverage/htmlfiles/keybd_open.png coverage/htmlfiles/pyfile.html coverage/htmlfiles/style.css doc/api.rst doc/api_coverage.rst doc/api_coveragedata.rst doc/api_plugin.rst doc/branch.rst doc/changes.rst doc/cmd.rst doc/conf.py doc/config.rst doc/contributing.rst doc/dict.txt doc/excluding.rst doc/faq.rst doc/howitworks.rst doc/index.rst doc/install.rst doc/plugins.rst doc/python-coverage.1.txt doc/requirements.pip doc/source.rst doc/subprocess.rst doc/trouble.rst doc/_static/coverage.css requirements/ci.pip requirements/dev.pip requirements/pytest.pip requirements/tox.pip requirements/wheel.pip tests/__init__.py tests/backtest.py tests/conftest.py tests/coveragetest.py tests/covmodzip1.py tests/goldtest.py tests/helpers.py tests/osinfo.py tests/plugin1.py tests/plugin2.py tests/plugin_config.py tests/stress_phystoken.tok tests/stress_phystoken_dos.tok tests/test_api.py tests/test_arcs.py tests/test_backward.py tests/test_cmdline.py tests/test_collector.py tests/test_concurrency.py tests/test_config.py tests/test_coverage.py tests/test_data.py tests/test_debug.py tests/test_execfile.py tests/test_farm.py tests/test_filereporter.py tests/test_files.py tests/test_html.py tests/test_misc.py tests/test_oddball.py tests/test_parser.py tests/test_phystokens.py tests/test_pickle2json.py tests/test_plugins.py tests/test_process.py tests/test_python.py tests/test_results.py tests/test_setup.py tests/test_summary.py tests/test_templite.py tests/test_testing.py tests/test_version.py tests/test_xml.py tests/eggsrc/setup.py tests/eggsrc/egg1/__init__.py tests/eggsrc/egg1/egg1.py tests/farm/annotate/annotate_dir.py tests/farm/annotate/run.py tests/farm/annotate/run_encodings.py tests/farm/annotate/run_multi.py tests/farm/annotate/gold/white.py,cover tests/farm/annotate/gold_anno_dir/a___init__.py,cover tests/farm/annotate/gold_anno_dir/a_a.py,cover tests/farm/annotate/gold_anno_dir/b___init__.py,cover tests/farm/annotate/gold_anno_dir/b_b.py,cover tests/farm/annotate/gold_anno_dir/multi.py,cover tests/farm/annotate/gold_encodings/utf8.py,cover tests/farm/annotate/gold_multi/multi.py,cover tests/farm/annotate/gold_multi/a/__init__.py,cover tests/farm/annotate/gold_multi/a/a.py,cover tests/farm/annotate/gold_multi/b/__init__.py,cover tests/farm/annotate/gold_multi/b/b.py,cover tests/farm/annotate/src/multi.py tests/farm/annotate/src/utf8.py tests/farm/annotate/src/white.py tests/farm/annotate/src/a/__init__.py tests/farm/annotate/src/a/a.py tests/farm/annotate/src/b/__init__.py tests/farm/annotate/src/b/b.py tests/farm/html/gold_a/a_py.html tests/farm/html/gold_a/index.html tests/farm/html/gold_b_branch/b_py.html tests/farm/html/gold_b_branch/index.html tests/farm/html/gold_bom/bom_py.html tests/farm/html/gold_bom/index.html tests/farm/html/gold_isolatin1/index.html tests/farm/html/gold_isolatin1/isolatin1_py.html tests/farm/html/gold_omit_1/index.html tests/farm/html/gold_omit_1/m1_py.html tests/farm/html/gold_omit_1/m2_py.html tests/farm/html/gold_omit_1/m3_py.html tests/farm/html/gold_omit_1/main_py.html tests/farm/html/gold_omit_2/index.html tests/farm/html/gold_omit_2/m2_py.html tests/farm/html/gold_omit_2/m3_py.html tests/farm/html/gold_omit_2/main_py.html tests/farm/html/gold_omit_3/index.html tests/farm/html/gold_omit_3/m3_py.html tests/farm/html/gold_omit_3/main_py.html tests/farm/html/gold_omit_4/index.html tests/farm/html/gold_omit_4/m1_py.html tests/farm/html/gold_omit_4/m3_py.html tests/farm/html/gold_omit_4/main_py.html tests/farm/html/gold_omit_5/index.html tests/farm/html/gold_omit_5/m1_py.html tests/farm/html/gold_omit_5/main_py.html tests/farm/html/gold_other/blah_blah_other_py.html tests/farm/html/gold_other/here_py.html tests/farm/html/gold_other/index.html tests/farm/html/gold_partial/index.html tests/farm/html/gold_partial/partial_py.html tests/farm/html/gold_styled/a_py.html tests/farm/html/gold_styled/extra.css tests/farm/html/gold_styled/index.html tests/farm/html/gold_styled/style.css tests/farm/html/gold_unicode/index.html tests/farm/html/gold_unicode/unicode_py.html tests/farm/html/gold_x_xml/coverage.xml tests/farm/html/gold_y_xml_branch/coverage.xml tests/farm/html/othersrc/other.py tests/farm/html/src/a.py tests/farm/html/src/b.py tests/farm/html/src/bom.py tests/farm/html/src/extra.css tests/farm/html/src/here.py tests/farm/html/src/isolatin1.py tests/farm/html/src/m1.py tests/farm/html/src/m2.py tests/farm/html/src/m3.py tests/farm/html/src/main.py tests/farm/html/src/omit4.ini tests/farm/html/src/omit5.ini tests/farm/html/src/partial.ini tests/farm/html/src/partial.py tests/farm/html/src/run_a_xml_2.ini tests/farm/html/src/tabbed.py tests/farm/html/src/unicode.py tests/farm/html/src/y.py tests/farm/run/run_chdir.py tests/farm/run/run_timid.py tests/farm/run/run_xxx.py tests/farm/run/src/chdir.py tests/farm/run/src/showtrace.py tests/farm/run/src/xxx tests/farm/run/src/subdir/placeholder tests/js/index.html tests/js/tests.js tests/modules/covmod1.py tests/modules/runmod1.py tests/modules/usepkgs.py tests/modules/aa/__init__.py tests/modules/aa/afile.odd.py tests/modules/aa/afile.py tests/modules/aa/zfile.py tests/modules/aa/bb/__init__.py tests/modules/aa/bb/bfile.odd.py tests/modules/aa/bb/bfile.py tests/modules/aa/bb.odd/bfile.py tests/modules/aa/bb/cc/__init__.py tests/modules/aa/bb/cc/cfile.py tests/modules/namespace_420/sub1/__init__.py tests/modules/pkg1/__init__.py tests/modules/pkg1/__main__.py tests/modules/pkg1/p1a.py tests/modules/pkg1/p1b.py tests/modules/pkg1/p1c.py tests/modules/pkg1/runmod2.py tests/modules/pkg1/sub/__init__.py tests/modules/pkg1/sub/__main__.py tests/modules/pkg1/sub/ps1a.py tests/modules/pkg1/sub/runmod3.py tests/modules/pkg2/__init__.py tests/modules/pkg2/p2a.py tests/modules/pkg2/p2b.py tests/modules/plugins/__init__.py tests/modules/plugins/a_plugin.py tests/modules/plugins/another.py tests/modules/process_test/__init__.py tests/modules/process_test/try_execfile.py tests/moremodules/namespace_420/sub2/__init__.py tests/moremodules/othermods/__init__.py tests/moremodules/othermods/othera.py tests/moremodules/othermods/otherb.py tests/moremodules/othermods/sub/__init__.py tests/moremodules/othermods/sub/osa.py tests/moremodules/othermods/sub/osb.py tests/qunit/jquery.tmpl.min.jspython-coverage-4.5+dfsg.1.orig/coverage.egg-info/PKG-INFO0000644000076600000620000001450113235414513021042 0ustar staffMetadata-Version: 1.2 Name: coverage Version: 4.5 Summary: Code coverage measurement for Python Home-page: https://bitbucket.org/ned/coveragepy Author: Ned Batchelder and 100 others Author-email: ned@nedbatchelder.com License: Apache 2.0 Description-Content-Type: UNKNOWN Description: .. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt =========== Coverage.py =========== Code coverage testing for Python. | |license| |versions| |status| |docs| | |ci-status| |win-ci-status| |codecov| | |kit| |format| |saythanks| .. downloads badge seems to be broken... |downloads| Coverage.py measures code coverage, typically during test execution. It uses the code analysis tools and tracing hooks provided in the Python standard library to determine which lines are executable, and which have been executed. Coverage.py runs on many versions of Python: * CPython 2.6, 2.7 and 3.3 through 3.7. * PyPy2 5.10 and PyPy3 5.10. * Jython 2.7.1, though not for reporting. * IronPython 2.7.7, though not for reporting. Documentation is on `Read the Docs`_. Code repository and issue tracker are on `Bitbucket`_, with a mirrored repository on `GitHub`_. .. _Read the Docs: https://coverage.readthedocs.io/ .. _Bitbucket: https://bitbucket.org/ned/coveragepy .. _GitHub: https://github.com/nedbat/coveragepy **New in 4.5:** Configurator plug-ins. New in 4.4: Suppressable warnings, continuous coverage measurement. New in 4.3: HTML ``--skip-covered``, sys.excepthook support, tox.ini support. New in 4.2: better support for multiprocessing and combining data. New in 4.1: much-improved branch coverage. New in 4.0: ``--concurrency``, plugins for non-Python files, setup.cfg support, --skip-covered, HTML filtering, and more than 50 issues closed. Getting Started --------------- See the `Quick Start section`_ of the docs. .. _Quick Start section: https://coverage.readthedocs.io/#quick-start Contributing ------------ See the `Contributing section`_ of the docs. .. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html License ------- Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_. .. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 .. _NOTICE.txt: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. |ci-status| image:: https://travis-ci.org/nedbat/coveragepy.svg?branch=master :target: https://travis-ci.org/nedbat/coveragepy :alt: Build status .. |win-ci-status| image:: https://ci.appveyor.com/api/projects/status/kmeqpdje7h9r6vsf/branch/master?svg=true :target: https://ci.appveyor.com/project/nedbat/coveragepy :alt: Windows build status .. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat :target: https://coverage.readthedocs.io/ :alt: Documentation .. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master :alt: Requirements status .. |kit| image:: https://badge.fury.io/py/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: PyPI status .. |format| image:: https://img.shields.io/pypi/format/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Kit format .. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Weekly PyPI downloads .. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Python versions supported .. |status| image:: https://img.shields.io/pypi/status/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: Package stability .. |license| image:: https://img.shields.io/pypi/l/coverage.svg :target: https://pypi.python.org/pypi/coverage :alt: License .. |codecov| image:: http://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2 :target: http://codecov.io/github/nedbat/coveragepy?branch=master :alt: Coverage! .. |saythanks| image:: https://img.shields.io/badge/saythanks.io-%E2%98%BC-1EAEDB.svg :target: https://saythanks.io/to/nedbat :alt: Say thanks :) Keywords: code coverage testing Platform: UNKNOWN Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Programming Language :: Python :: Implementation :: Jython Classifier: Programming Language :: Python :: Implementation :: IronPython Classifier: Topic :: Software Development :: Quality Assurance Classifier: Topic :: Software Development :: Testing Classifier: Development Status :: 5 - Production/Stable Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4 python-coverage-4.5+dfsg.1.orig/coverage.egg-info/dependency_links.txt0000644000076600000620000000000113235414513024012 0ustar staff python-coverage-4.5+dfsg.1.orig/coverage.egg-info/not-zip-safe0000644000076600000620000000000113235414513022172 0ustar staff python-coverage-4.5+dfsg.1.orig/tox_wheels.ini0000644000076600000620000000100513146037571017343 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt [tox] envlist = py{26,27,33,34,35,36,sys} toxworkdir = {toxinidir}/.tox_kits [testenv] deps = -rrequirements/wheel.pip commands = python -c "import sys; print(sys.real_prefix)" python setup.py bdist_wheel {posargs} [testenv:py27] basepython = python2.7 [testenv:pysys] # For building with the Mac Framework Python. basepython = /usr/bin/python python-coverage-4.5+dfsg.1.orig/doc/0000755000076600000620000000000013235414514015225 5ustar staffpython-coverage-4.5+dfsg.1.orig/doc/requirements.pip0000644000076600000620000000045513231641315020463 0ustar staff# PyPI requirements for building documentation for coverage.py # https://requires.io/github/nedbat/coveragepy/requirements/ pyenchant==2.0.0 sphinx==1.6.6 sphinxcontrib-spelling==4.0.1 sphinx_rtd_theme==0.2.4 # A version of doc8 with a -q flag. git+https://github.com/nedbat/doc8.git#egg=doc8==0.0 python-coverage-4.5+dfsg.1.orig/doc/excluding.rst0000644000076600000620000000703713146037571017755 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _excluding: =============================== Excluding code from coverage.py =============================== .. :history: 20090613T090500, brand new docs. .. :history: 20100224T200900, updated for 3.3. .. :history: 20100725T211700, updated for 3.4. .. :history: 20110604T184400, updated for 3.5. You may have code in your project that you know won't be executed, and you want to tell coverage.py to ignore it. For example, you may have debugging-only code that won't be executed during your unit tests. You can tell coverage.py to exclude this code during reporting so that it doesn't clutter your reports with noise about code that you don't need to hear about. Coverage.py will look for comments marking clauses for exclusion. In this code, the "if debug" clause is excluded from reporting:: a = my_function1() if debug: # pragma: no cover msg = "blah blah" log_message(msg, a) b = my_function2() Any line with a comment of "pragma: no cover" is excluded. If that line introduces a clause, for example, an if clause, or a function or class definition, then the entire clause is also excluded. Here the __repr__ function is not reported as missing:: class MyObject(object): def __init__(self): blah1() blah2() def __repr__(self): # pragma: no cover return "" Excluded code is executed as usual, and its execution is recorded in the coverage data as usual. When producing reports though, coverage.py excludes it from the list of missing code. Branch coverage --------------- When measuring :ref:`branch coverage `, a conditional will not be counted as a branch if one of its choices is excluded:: def only_one_choice(x): if x: blah1() blah2() else: # pragma: no cover # x is always true. blah3() Because the ``else`` clause is excluded, the ``if`` only has one possible next line, so it isn't considered a branch at all. Advanced exclusion ------------------ Coverage.py identifies exclusions by matching lines against a list of regular expressions. Using :ref:`configuration files ` or the coverage :ref:`API `, you can add to that list. This is useful if you have often-used constructs to exclude that can be matched with a regex. You can exclude them all at once without littering your code with exclusion pragmas. For example, you might decide that __repr__ functions are usually only used in debugging code, and are uninteresting to test themselves. You could exclude all of them by adding a regex to the exclusion list:: [report] exclude_lines = def __repr__ For example, here's a list of exclusions I've used:: [report] exclude_lines = pragma: no cover def __repr__ if self.debug: if settings.DEBUG raise AssertionError raise NotImplementedError if 0: if __name__ == .__main__.: Note that when using the ``exclude_lines`` option in a configuration file, you are taking control of the entire list of regexes, so you need to re-specify the default "pragma: no cover" match if you still want it to apply. A similar pragma, "no branch", can be used to tailor branch coverage measurement. See :ref:`branch` for details. Excluding source files ---------------------- See :ref:`source` for ways to limit what files coverage.py measures or reports on. python-coverage-4.5+dfsg.1.orig/doc/trouble.rst0000644000076600000620000000562713177624110017445 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _trouble: ========================= Things that cause trouble ========================= .. :history: 20121231T085200, brand new docs. .. :history: 20150124T160800, remove obsolete stuff. Coverage.py works well, and I want it to properly measure any Python program, but there are some situations it can't cope with. This page details some known problems, with possible courses of action, and links to coverage.py bug reports with more information. I would love to :ref:`hear from you ` if you have information about any of these problems, even just to explain to me why you want them to start working properly. If your problem isn't discussed here, you can of course search the `coverage.py bug tracker`_ directly to see if there is some mention of it. .. _coverage.py bug tracker: https://bitbucket.org/ned/coveragepy/issues?status=new&status=open Things that don't work ---------------------- There are a number of popular modules, packages, and libraries that prevent coverage.py from working properly: * `execv`_, or one of its variants. These end the current program and replace it with a new one. This doesn't save the collected coverage data, so your program that calls execv will not be fully measured. A patch for coverage.py is in `issue 43`_. * `thread`_, in the Python standard library, is the low-level threading interface. Threads created with this module will not be traced. Use the higher-level `threading`_ module instead. * `sys.settrace`_ is the Python feature that coverage.py uses to see what's happening in your program. If another part of your program is using sys.settrace, then it will conflict with coverage.py, and it won't be measured properly. .. _execv: https://docs.python.org/3/library/os.html#os.execl .. _sys.settrace: https://docs.python.org/3/library/sys.html#sys.settrace .. _thread: https://docs.python.org/3/library/_thread.html .. _threading: https://docs.python.org/3/library/threading.html .. _issue 43: https://bitbucket.org/ned/coveragepy/issues/43/coverage-measurement-fails-on-code Things that require --timid --------------------------- Some packages interfere with coverage measurement, but you might be able to make it work by using the ``--timid`` command-line switch, or the ``[run] timid=True`` configuration option. * `DecoratorTools`_, or any package which uses it, notably `TurboGears`_. DecoratorTools fiddles with the trace function. You will need to use ``--timid``. .. _DecoratorTools: https://pypi.python.org/pypi/DecoratorTools .. _TurboGears: http://turbogears.org/ Still having trouble? --------------------- If your problem isn't mentioned here, and isn't already reported in the `coverage.py bug tracker`_, please :ref:`get in touch with me `, we'll figure out a solution. python-coverage-4.5+dfsg.1.orig/doc/api_coveragedata.rst0000644000076600000620000000061313146037571021242 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _api_coveragedata: The CoverageData class ---------------------- .. :history: 20150802T174800, new doc for 4.0b1 .. versionadded:: 4.0 .. module:: coverage .. autoclass:: CoverageData :members: :special-members: __init__ python-coverage-4.5+dfsg.1.orig/doc/index.rst0000644000076600000620000001575713235412633017105 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt =========== Coverage.py =========== .. :history: 20090524T134300, brand new docs. .. :history: 20090613T164000, final touches for 3.0 .. :history: 20090618T195900, minor tweaks .. :history: 20090707T205200, changes for 3.0.1 .. :history: 20090913T084400, new command line syntax .. :history: 20091004T211900, version 3.1 .. :history: 20091127T155100, version 3.2 .. :history: 20091205T161429, version 3.2 for real. .. :history: 20100224T204700, version 3.3 .. :history: 20100306T181500, version 3.3.1 .. :history: 20100725T211700, updated for 3.4. .. :history: 20100820T151500, updated for 3.4b1. .. :history: 20100906T134700, updated for 3.4b2. .. :history: 20100919T163500, updated for 3.4 release. .. :history: 20110213T081200, claim true 3.2 compatibility. .. :history: 20110604T114800, update for 3.5b1 .. :history: 20110629T082300, update for 3.5 .. :history: 20110827T221800, update for 3.5.1b1 .. :history: 20110923T081800, update for 3.5.1 .. :history: 20120429T162100, updated for 3.5.2b1 .. :history: 20120503T233800, updated for 3.5.2 .. :history: 20120929T093500, updated for 3.5.3 .. :history: 20121117T094900, Change from easy_install to pip. .. :history: 20121128T203700, Updated for 3.6b1. .. :history: 20121223T180600, Updated for 3.6b2. .. :history: 20121229T112300, Updated for 3.6b3. .. :history: 20130105T174000, Updated for 3.6 .. :history: 20131005T210000, Updated for 3.7 .. :history: 20131212T213300, Updated for 3.7.1 .. :history: 20140924T073000, Updated for 4.0a1 .. :history: 20150124T023900, Updated for 4.0a4 .. :history: 20150216T201000, Updated for 4.0a5 .. :history: 20150802T160200, Updated for 4.0b1 .. :history: 20150822T092900, Updated for 4.0b2 .. :history: 20150918T072700, Updated for 4.0 .. :history: 20151013T103200, Updated for 4.0.1 .. :history: 20151104T050900, updated for 4.0.2 .. :history: 20151124T065900, updated for 4.0.3 .. :history: 20160110T125900, updated for 4.1b1 .. :history: 20160123T171300, updated for 4.1b2 .. :history: 20160510T125300, updated for 4.1b3 .. :history: 20160521T074500, updated for 4.1 .. :history: 20160726T161300, updated for 4.2 .. :history: 20161226T160400, updated for 4.3 .. :history: 20170116T180100, updated for 4.3.2 .. :history: 20180203T130300, updated for 4.5 Coverage.py is a tool for measuring code coverage of Python programs. It monitors your program, noting which parts of the code have been executed, then analyzes the source to identify code that could have been executed but was not. Coverage measurement is typically used to gauge the effectiveness of tests. It can show which parts of your code are being exercised by tests, and which are not. .. ifconfig:: not prerelease The latest version is coverage.py 4.5, released February 3rd 2018. It is supported on: * Python versions 2.6, 2.7, 3.3, 3.4, 3.5, 3.6, and 3.7. * PyPy2 5.10 and PyPy3 5.10. * Jython 2.7.1, though only for running code, not reporting. * IronPython 2.7.7, though only for running code, not reporting. .. ifconfig:: prerelease The latest version is coverage.py 4.4b1, released April 4th 2017. It is supported on: * Python versions 2.6, 2.7, 3.3, 3.4, 3.5, and 3.6. * PyPy2 5.6 and PyPy3 5.5. * Jython 2.7.1, though only for running code, not reporting. * IronPython 2.7.7, though only for running code, not reporting. **This is a pre-release build. The usual warnings about possible bugs apply.** The latest stable version is coverage.py 4.3.4, `described here`_. .. _described here: https://nedbatchelder.com/code/coverage Quick start ----------- Getting started is easy: #. Install coverage.py from the `coverage.py page on the Python Package Index`_, or by using "pip install coverage". For a few more details, see :ref:`install`. #. Use ``coverage run`` to run your program and gather data: .. code-block:: console # if you usually do: # # $ python my_program.py arg1 arg2 # # then instead do: $ coverage run my_program.py arg1 arg2 blah blah ..your program's output.. blah blah #. Use ``coverage report`` to report on the results: .. code-block:: console $ coverage report -m Name Stmts Miss Cover Missing ------------------------------------------------------- my_program.py 20 4 80% 33-35, 39 my_other_module.py 56 6 89% 17-23 ------------------------------------------------------- TOTAL 76 10 87% #. For a nicer presentation, use ``coverage html`` to get annotated HTML listings detailing missed lines: .. code-block:: console $ coverage html .. ifconfig:: not prerelease Then visit htmlcov/index.html in your browser, to see a `report like this`_. .. ifconfig:: prerelease Then visit htmlcov/index.html in your browser, to see a `report like this one`_. .. _coverage.py page on the Python Package Index: https://pypi.python.org/pypi/coverage .. _report like this: https://nedbatchelder.com/files/sample_coverage_html/index.html .. _report like this one: https://nedbatchelder.com/files/sample_coverage_html_beta/index.html Using coverage.py ----------------- There are a few different ways to use coverage.py. The simplest is the :ref:`command line `, which lets you run your program and see the results. If you need more control over how your project is measured, you can use the :ref:`API `. Some test runners provide coverage integration to make it easy to use coverage.py while running tests. For example, `pytest`_ has the `pytest-cov`_ plugin. You can fine-tune coverage.py's view of your code by directing it to ignore parts that you know aren't interesting. See :ref:`source` and :ref:`excluding` for details. .. _pytest: http://doc.pytest.org .. _pytest-cov: https://pytest-cov.readthedocs.io/ .. _contact: Getting help ------------ If the :ref:`FAQ ` doesn't answer your question, you can discuss coverage.py or get help using it on the `Testing In Python`_ mailing list. .. _Testing In Python: http://lists.idyll.org/listinfo/testing-in-python Bug reports are gladly accepted at the `Bitbucket issue tracker`_. Bitbucket also hosts the `code repository`_. There is a `mirrored repo`_ on GitHub. .. _Bitbucket issue tracker: https://bitbucket.org/ned/coveragepy/issues .. _code repository: https://bitbucket.org/ned/coveragepy .. _mirrored repo: https://github.com/nedbat/coveragepy `I can be reached`_ in a number of ways. I'm happy to answer questions about using coverage.py. .. _I can be reached: https://nedbatchelder.com/site/aboutned.html More information ---------------- .. toctree:: :maxdepth: 1 install cmd config source excluding branch subprocess api howitworks plugins contributing trouble faq changes python-coverage-4.5+dfsg.1.orig/doc/branch.rst0000644000076600000620000001032413146037571017221 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _branch: =========================== Branch coverage measurement =========================== .. :history: 20091127T201300, new for version 3.2 .. :history: 20100725T211700, updated for 3.4. .. :history: 20110604T181700, updated for 3.5. .. :history: 20111214T181800, Fix a bug that Guido pointed out. .. highlight:: python :linenothreshold: 5 In addition to the usual statement coverage, coverage.py also supports branch coverage measurement. Where a line in your program could jump to more than one next line, coverage.py tracks which of those destinations are actually visited, and flags lines that haven't visited all of their possible destinations. For example:: def my_partial_fn(x): # line 1 if x: # 2 y = 10 # 3 return y # 4 my_partial_fn(1) In this code, line 2 is an ``if`` statement which can go next to either line 3 or line 4. Statement coverage would show all lines of the function as executed. But the if was never evaluated as false, so line 2 never jumps to line 4. Branch coverage will flag this code as not fully covered because of the missing jump from line 2 to line 4. This is known as a partial branch. How to measure branch coverage ------------------------------ To measure branch coverage, run coverage.py with the ``--branch`` flag:: coverage run --branch myprog.py When you report on the results with ``coverage report`` or ``coverage html``, the percentage of branch possibilities taken will be included in the percentage covered total for each file. The coverage percentage for a file is the actual executions divided by the execution opportunities. Each line in the file is an execution opportunity, as is each branch destination. The HTML report gives information about which lines had missing branches. Lines that were missing some branches are shown in yellow, with an annotation at the far right showing branch destination line numbers that were not exercised. The XML report produced by ``coverage xml`` also includes branch information, including separate statement and branch coverage percentages. How it works ------------ When measuring branches, coverage.py collects pairs of line numbers, a source and destination for each transition from one line to another. Static analysis of the source provides a list of possible transitions. Comparing the measured to the possible indicates missing branches. The idea of tracking how lines follow each other was from `Titus Brown`__. Thanks, Titus! __ http://ivory.idyll.org/blog Excluding code -------------- If you have :ref:`excluded code `, a conditional will not be counted as a branch if one of its choices is excluded:: def only_one_choice(x): if x: blah1() blah2() else: # pragma: no cover # x is always true. blah3() Because the ``else`` clause is excluded, the ``if`` only has one possible next line, so it isn't considered a branch at all. Structurally partial branches ----------------------------- Sometimes branching constructs are used in unusual ways that don't actually branch. For example:: while True: if cond: break do_something() Here the while loop will never exit normally, so it doesn't take both of its "possible" branches. For some of these constructs, such as "while True:" and "if 0:", coverage.py understands what is going on. In these cases, the line will not be marked as a partial branch. But there are many ways in your own code to write intentionally partial branches, and you don't want coverage.py pestering you about them. You can tell coverage.py that you don't want them flagged by marking them with a pragma:: i = 0 while i < 999999999: # pragma: no branch if eventually(): break Here the while loop will never complete because the break will always be taken at some point. Coverage.py can't work that out on its own, but the "no branch" pragma indicates that the branch is known to be partial, and the line is not flagged. python-coverage-4.5+dfsg.1.orig/doc/conf.py0000644000076600000620000001452513235413456016537 0ustar staff# -*- coding: utf-8 -*- # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # # coverage.py documentation build configuration file, created by # sphinx-quickstart on Wed May 13 22:18:33 2009. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # on_rtd is whether we are on readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.ifconfig', 'sphinxcontrib.spelling', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Coverage.py' copyright = u'2009\N{EN DASH}2018, Ned Batchelder' # CHANGEME # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '4.5' # CHANGEME # The full version, including alpha/beta/rc tags. release = '4.5' # CHANGEME # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. #html_theme = 'default' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # otherwise, readthedocs.org uses their theme by default, so no need to specify it # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} #html_style = "neds.css" #html_add_permalinks = "" # Add any paths that contain custom themes here, relative to this directory. html_theme_path = ['_templates'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. html_use_modindex = False # If false, no index is generated. html_use_index = False # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '.htm' # Output file base name for HTML help builder. htmlhelp_basename = 'coveragepydoc' # -- Spelling --- spelling_word_list_filename = 'dict.txt' spelling_show_suggestions = False # When auto-doc'ing a class, write the class' docstring and the __init__ docstring # into the class docs. autoclass_content = "class" prerelease = bool(max(release).isalpha()) def setup(app): app.add_stylesheet('coverage.css') app.add_config_value('prerelease', False, 'env') app.info("** Prerelease = %r" % prerelease) python-coverage-4.5+dfsg.1.orig/doc/api.rst0000644000076600000620000000224713146037571016542 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _api: =============== Coverage.py API =============== .. :history: 20090524T134300, brand new docs. .. :history: 20090613T164000, final touches for 3.0 .. :history: 20100221T151500, docs for 3.3 (on the plane back from PyCon) .. :history: 20100725T211700, updated for 3.4. .. :history: 20121111T235800, added a bit of clarification. .. :history: 20140819T132600, change class name to Coverage The API to coverage.py is very simple, contained in a module called `coverage`. Most of the interface is in the :class:`coverage.Coverage` class. Methods on the Coverage object correspond roughly to operations available in the command line interface. For example, a simple use would be:: import coverage cov = coverage.Coverage() cov.start() # .. call your code .. cov.stop() cov.save() cov.html_report() The :class:`coverage.CoverageData` class provides access to coverage data stored in coverage.py data files. .. toctree:: :maxdepth: 1 api_coverage api_coveragedata api_plugin python-coverage-4.5+dfsg.1.orig/doc/howitworks.rst0000644000076600000620000000705213177624110020203 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _howitworks: ===================== How Coverage.py works ===================== .. :history: 20150812T071000, new page. For advanced use of coverage.py, or just because you are curious, it helps to understand what's happening behind the scenes. Coverage.py works in three phases: * **Execution**: Coverage.py runs your code, and monitors it to see what lines were executed. * **Analysis**: Coverage.py examines your code to determine what lines could have run. * **Reporting**: Coverage.py combines the results of execution and analysis to produce a coverage number and an indication of missing execution. The execution phase is handled by the ``coverage run`` command. The analysis and reporting phases are handled by the reporting commands like ``coverage report`` or ``coverage html``. Let's look at each phase in more detail. Execution --------- At the heart of the execution phase is a Python trace function. This is a function that the Python interpreter invokes for each line executed in a program. Coverage.py implements a trace function that records each file and line number as it is executed. Executing a function for every line in your program can make execution very slow. Coverage.py's trace function is implemented in C to reduce that slowdown. It also takes care to not trace code that you aren't interested in. When measuring branch coverage, the same trace function is used, but instead of recording line numbers, coverage.py records pairs of line numbers. Each invocation of the trace function remembers the line number, then the next invocation records the pair `(prev, this)` to indicate that execution transitioned from the previous line to this line. Internally, these are called arcs. For more details of trace functions, see the Python docs for `sys.settrace`_, or if you are really brave, `How C trace functions really work`_. At the end of execution, coverage.py writes the data it collected to a data file, usually named ``.coverage``. This is a JSON-based file containing all of the recorded file names and line numbers executed. .. _sys.settrace: https://docs.python.org/3/library/sys.html#sys.settrace .. _How C trace functions really work: https://nedbatchelder.com/text/trace-function.html Analysis -------- After your program has been executed and the line numbers recorded, coverage.py needs to determine what lines could have been executed. Luckily, compiled Python files (.pyc files) have a table of line numbers in them. Coverage.py reads this table to get the set of executable lines, with a little more source analysis to leave out things like docstrings. The data file is read to get the set of lines that were executed. The difference between the executable lines, and the executed lines, are the lines that were not executed. The same principle applies for branch measurement, though the process for determining possible branches is more involved. Coverage.py uses the abstract syntax tree of the Python source file to determine the set of possible branches. Reporting --------- Once we have the set of executed lines and missing lines, reporting is just a matter of formatting that information in a useful way. Each reporting method (text, html, annotated source, xml) has a different output format, but the process is the same: write out the information in the particular format, possibly including the source code itself. Plugins ------- Plugins interact with these phases. python-coverage-4.5+dfsg.1.orig/doc/dict.txt0000644000076600000620000000342713146037571016724 0ustar staffactivestate api apache API args argv basename basenames bitbucket BOM bom boolean booleans BTW btw builtin builtins bytecode bytecodes bytestring canonicalize canonicalized canonicalizes chdir'd cmdline Cobertura codecs colorsys config configparser configurability configurability's cov coveragepy coveragerc covhtml CPython css CTracer datetime dedent dict dict's dicts dirname django docstring docstrings doctest DOCTYPE DOM encodings endfor endif eventlet exe exec'd exec'ing execfile executable's expr filename filenames filepath fname fnmatch fooey fpath fullcoverage getattr gevent gevent's github globals greenlet hotkey hotkeys html HTML htmlcov http https importlib instancemethod int ints invariants iterable iterables json jython kwargs matcher matchers merchantability metadata meth mixin modulename monkeypatch monkeypatching morf morfs multi mumbo mycode namespace namespaces nano nbsp ned nedbat nedbatchelder nosetests nullary num ok OK opcode opcodes optparse os outfile overridable parsable parsers pathnames pragma pragmas pre prepended prepending programmability programmatically py py's pyc pyexpat pylint pypy pythonpath PYTHONPATH pyw rcfile readme refactored regex regexes renderer serializable settrace setuptools sitecustomize sortable src stackoverflow stderr stdlib stdout str sys templite templating timestamp todo TODO tokenization tokenize tokenized tokenizer tokenizes tokenizing tox traceback tracebacks tuple tuples txt ubuntu undecodable unexecuted unicode unittest unparsable unsubscriptable untokenizable URL UTF utf versionadded virtualenv whitespace wikipedia wildcard wildcards www xml XML xrange xyzzy Adi Berezhny Berman Betz Carballo Cobden Danek Dmitry Duvall Finney Gedminas Jeanpierre Jevnik Kichewko Krystian Lex Lytwynec Olds Portante Roiban Schettino Trofimov Zooko python-coverage-4.5+dfsg.1.orig/doc/faq.rst0000644000076600000620000001235713224244721016535 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _faq: ================== FAQ and other help ================== Frequently asked questions -------------------------- **Q: How do I use coverage.py with nose?** The best way to use nose and coverage.py together is to run nose under coverage.py:: coverage run $(which nosetests) You can also use ``nosetests --with-coverage`` to use `nose's built-in plugin`__, but it isn't recommended. The nose plugin doesn't expose all the functionality and configurability of coverage.py, and it uses different command-line options from those described in coverage.py's documentation. Additionally nose and its coverage plugin are unmaintained at this point, so they aren't receiving any fixes or other updates. __ https://nose.readthedocs.io/en/latest/plugins/cover.html **Q: How do I run nosetests under coverage.py with tox?** Assuming you've installed tox in a virtualenv, you can do this in tox.ini:: [testenv] commands = coverage run {envbindir}/nosetests Coverage.py needs a path to the nosetests executable, but ``coverage run $(which nosetests)`` doesn't work in tox.ini because tox doesn't handle the shell command substitution. Tox's `string substitution`__ shown above does the trick. __ https://tox.readthedocs.io/en/latest/config.html#substitutions **Q: I use nose to run my tests, and its coverage plugin doesn't let me create HTML or XML reports. What should I do?** First run your tests and collect coverage data with `nose`_ and its plugin. This will write coverage data into a .coverage file. Then run coverage.py from the :ref:`command line ` to create the reports you need from that data. .. _nose: https://nose.readthedocs.io/ **Q: Why do unexecutable lines show up as executed?** Usually this is because you've updated your code and run coverage.py on it again without erasing the old data. Coverage.py records line numbers executed, so the old data may have recorded a line number which has since moved, causing coverage.py to claim a line has been executed which cannot be. If you are using the ``-x`` command line action, it doesn't erase first by default. Switch to the ``coverage run`` command, or use the ``-e`` switch to erase all data before starting the next run. **Q: Why do the bodies of functions (or classes) show as executed, but the def lines do not?** This happens because coverage.py is started after the functions are defined. The definition lines are executed without coverage measurement, then coverage.py is started, then the function is called. This means the body is measured, but the definition of the function itself is not. To fix this, start coverage.py earlier. If you use the :ref:`command line ` to run your program with coverage.py, then your entire program will be monitored. If you are using the :ref:`API `, you need to call coverage.start() before importing the modules that define your functions. **Q: Coverage.py is much slower than I remember, what's going on?** Make sure you are using the C trace function. Coverage.py provides two implementations of the trace function. The C implementation runs much faster. To see what you are running, use ``coverage debug sys``. The output contains details of the environment, including a line that says either ``tracer: CTracer`` or ``tracer: PyTracer``. If it says ``PyTracer`` then you are using the slow Python implementation. Try re-installing coverage.py to see what happened and if you get the CTracer as you should. **Q: Isn't coverage testing the best thing ever?** It's good, but `it isn't perfect`__. __ https://nedbatchelder.com/blog/200710/flaws_in_coverage_measurement.html .. Other resources --------------- There are a number of projects that help integrate coverage.py into other systems: - `trialcoverage`_ is a plug-in for Twisted trial. .. _trialcoverage: https://pypi.python.org/pypi/trialcoverage - `pytest-coverage`_ .. _pytest-coverage: https://pypi.python.org/pypi/pytest-coverage - `django-coverage`_ for use with Django. .. _django-coverage: https://pypi.python.org/pypi/django-coverage **Q: Where can I get more help with coverage.py?** You can discuss coverage.py or get help using it on the `Testing In Python`_ mailing list. .. _Testing In Python: http://lists.idyll.org/listinfo/testing-in-python Bug reports are gladly accepted at the `Bitbucket issue tracker`_. .. _Bitbucket issue tracker: https://bitbucket.org/ned/coveragepy/issues Announcements of new coverage.py releases are sent to the `coveragepy-announce`_ mailing list. .. _coveragepy-announce: http://groups.google.com/group/coveragepy-announce `I can be reached`__ in a number of ways, I'm happy to answer questions about using coverage.py. __ https://nedbatchelder.com/site/aboutned.html History ------- Coverage.py was originally written by `Gareth Rees`_. Since 2004, `Ned Batchelder`_ has extended and maintained it with the help of `many others`_. The :ref:`change history ` has all the details. .. _Gareth Rees: http://garethrees.org/ .. _Ned Batchelder: https://nedbatchelder.com .. _many others: https://bitbucket.org/ned/coveragepy/src/tip/CONTRIBUTORS.txt python-coverage-4.5+dfsg.1.orig/doc/install.rst0000644000076600000620000001005013177624110017421 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _install: ============ Installation ============ .. :history: 20100725T225600, new for 3.4. .. :history: 20100820T151500, updated for 3.4b1. .. :history: 20100906T134800, updated for 3.4b2. .. :history: 20110604T213400, updated for 3.5b1. .. :history: 20110629T082400, updated for 3.5. .. :history: 20110923T081900, updated for 3.5.1. .. :history: 20120429T162500, updated for 3.5.2b1. .. :history: 20120503T234000, updated for 3.5.2. .. :history: 20120929T093600, updated for 3.5.3. .. :history: 20121117T095000, Now setuptools is a pre-req. .. :history: 20121128T203000, updated for 3.6b1. .. :history: 20121223T180800, updated for 3.6b2. .. :history: 20121229T112400, updated for 3.6b3. .. :history: 20130105T174400, updated for 3.6. .. :history: 20131005T210600, updated for 3.7. .. :history: 20131212T213500, updated for 3.7.1. .. :history: 20140927T102700, updated for 4.0a1. .. :history: 20161218T173000, remove alternate instructions w/ Distribute .. highlight:: console .. _coverage_pypi: https://pypi.python.org/pypi/coverage .. _setuptools: https://pypi.python.org/pypi/setuptools You can install coverage.py in the usual ways. The simplest way is with pip:: $ pip install coverage .. ifconfig:: prerelease To install a pre-release version, you will need to specify ``--pre``:: $ pip install --pre coverage .. _install_extension: C Extension ----------- Coverage.py includes a C extension for speed. It is strongly recommended to use this extension: it is much faster, and is needed to support a number of coverage.py features. Most of the time, the C extension will be installed without any special action on your part. If you are installing on Linux, you may need to install the python-dev and gcc support files before installing coverage via pip. The exact commands depend on which package manager you use, which Python version you are using, and the names of the packages for your distribution. For example:: $ sudo apt-get install python-dev gcc $ sudo yum install python-devel gcc $ sudo apt-get install python3-dev gcc $ sudo yum install python3-devel gcc You can determine if you are using the extension by looking at the output of ``coverage --version``:: $ coverage --version Coverage.py, version |release| with C extension Documentation at https://coverage.readthedocs.io The first line will either say "with C extension," or "without C extension." A few features of coverage.py aren't supported without the C extension, such as concurrency and plugins. Installing on Windows --------------------- For Windows, kits are provided on the `PyPI page`__ for different versions of Python and different CPU architectures. These kits require that `setuptools`_ be installed as a pre-requisite, but otherwise are self-contained. They have the C extension pre-compiled so there's no need to worry about compilers. .. __: coverage_pypi_ Checking the installation ------------------------- If all went well, you should be able to open a command prompt, and see coverage.py installed properly: .. ifconfig:: not prerelease .. parsed-literal:: $ coverage --version Coverage.py, version |release| with C extension Documentation at https://coverage.readthedocs.io .. ifconfig:: prerelease .. parsed-literal:: $ coverage --version Coverage.py, version |release| with C extension Documentation at https://coverage.readthedocs.io/en/coverage-|release| You can also invoke coverage.py as a module: .. ifconfig:: not prerelease .. parsed-literal:: $ python -m coverage --version Coverage.py, version |release| with C extension Documentation at https://coverage.readthedocs.io .. ifconfig:: prerelease .. parsed-literal:: $ python -m coverage --version Coverage.py, version |release| with C extension Documentation at https://coverage.readthedocs.io/en/coverage-|release| python-coverage-4.5+dfsg.1.orig/doc/subprocess.rst0000644000076600000620000000761413177624110020157 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _subprocess: ======================= Measuring sub-processes ======================= .. :history: 20100224T201800, new for 3.3. .. :history: 20100725T211700, updated for 3.4. Complex test suites may spawn sub-processes to run tests, either to run them in parallel, or because sub-process behavior is an important part of the system under test. Measuring coverage in those sub-processes can be tricky because you have to modify the code spawning the process to invoke coverage.py. There's an easier way to do it: coverage.py includes a function, :func:`coverage.process_startup` designed to be invoked when Python starts. It examines the ``COVERAGE_PROCESS_START`` environment variable, and if it is set, begins coverage measurement. The environment variable's value will be used as the name of the :ref:`configuration file ` to use. When using this technique, be sure to set the parallel option to true so that multiple coverage.py runs will each write their data to a distinct file. Configuring Python for sub-process coverage ------------------------------------------- Measuring coverage in sub-processes is a little tricky. When you spawn a sub-process, you are invoking Python to run your program. Usually, to get coverage measurement, you have to use coverage.py to run your program. Your sub-process won't be using coverage.py, so we have to convince Python to use coverage.py even when not explicitly invoked. To do that, we'll configure Python to run a little coverage.py code when it starts. That code will look for an environment variable that tells it to start coverage measurement at the start of the process. To arrange all this, you have to do two things: set a value for the ``COVERAGE_PROCESS_START`` environment variable, and then configure Python to invoke :func:`coverage.process_startup` when Python processes start. How you set ``COVERAGE_PROCESS_START`` depends on the details of how you create sub-processes. As long as the environment variable is visible in your sub-process, it will work. You can configure your Python installation to invoke the ``process_startup`` function in two ways: #. Create or append to sitecustomize.py to add these lines:: import coverage coverage.process_startup() #. Create a .pth file in your Python installation containing:: import coverage; coverage.process_startup() The sitecustomize.py technique is cleaner, but may involve modifying an existing sitecustomize.py, since there can be only one. If there is no sitecustomize.py already, you can create it in any directory on the Python path. The .pth technique seems like a hack, but works, and is documented behavior. On the plus side, you can create the file with any name you like so you don't have to coordinate with other .pth files. On the minus side, you have to create the file in a system-defined directory, so you may need privileges to write it. Note that if you use one of these techniques, you must undo them if you uninstall coverage.py, since you will be trying to import it during Python start-up. Be sure to remove the change when you uninstall coverage.py, or use a more defensive approach to importing it. Signal handlers and atexit -------------------------- .. hmm, this isn't specifically about subprocesses, is there a better place where we could talk about this? To successfully write a coverage data file, the Python sub-process under analysis must shut down cleanly and have a chance for coverage.py to run the ``atexit`` handler it registers. For example if you send SIGTERM to end the sub-process, but your sub-process has never registered any SIGTERM handler, then a coverage file won't be written. See the `atexit`_ docs for details of when the handler isn't run. .. _atexit: https://docs.python.org/3/library/atexit.html python-coverage-4.5+dfsg.1.orig/doc/config.rst0000644000076600000620000002377213231143243017231 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _config: =================== Configuration files =================== .. :history: 20100223T201600, new for 3.3 .. :history: 20100725T211700, updated for 3.4. .. :history: 20100824T092900, added ``precision``. .. :history: 20110604T184400, updated for 3.5. .. :history: 20110827T212700, updated for 3.5.1 .. :history: 20130926T222300, updated for 3.6.1 .. :history: 20140925T064700, updated for 4.0a1 .. :history: 20150124T173400, updated for 4.0a4 .. :history: 20150802T174600, updated for 4.0b1 .. module:: coverage Coverage.py options can be specified in a configuration file. This makes it easier to re-run coverage.py with consistent settings, and also allows for specification of options that are otherwise only available in the :ref:`API `. Configuration files also make it easier to get coverage testing of spawned sub-processes. See :ref:`subprocess` for more details. The default name for configuration files is ``.coveragerc``, in the same directory coverage.py is being run in. Most of the settings in the configuration file are tied to your source code and how it should be measured, so it should be stored with your source, and checked into source control, rather than put in your home directory. A different name for the configuration file can be specified with the ``--rcfile=FILE`` command line option. Coverage.py will read settings from other usual configuration files if no other configuration file is used. It will automatically read from "setup.cfg" or "tox.ini" if they exist. In this case, the section names have "coverage:" prefixed, so the ``[run]`` options described below will be found in the ``[coverage:run]`` section of the file. Syntax ------ A coverage.py configuration file is in classic .ini file format: sections are introduced by a ``[section]`` header, and contain ``name = value`` entries. Lines beginning with ``#`` or ``;`` are ignored as comments. Strings don't need quotes. Multi-valued strings can be created by indenting values on multiple lines. Boolean values can be specified as ``on``, ``off``, ``true``, ``false``, ``1``, or ``0`` and are case-insensitive. Environment variables can be substituted in by using dollar signs: ``$WORD`` or ``${WORD}`` will be replaced with the value of ``WORD`` in the environment. A dollar sign can be inserted with ``$$``. Missing environment variables will result in empty strings with no error. Many sections and values correspond roughly to commands and options in the :ref:`command-line interface `. Here's a sample configuration file:: # .coveragerc to control coverage.py [run] branch = True [report] # Regexes for lines to exclude from consideration exclude_lines = # Have to re-enable the standard pragma pragma: no cover # Don't complain about missing debug-only code: def __repr__ if self\.debug # Don't complain if tests don't hit defensive assertion code: raise AssertionError raise NotImplementedError # Don't complain if non-runnable code isn't run: if 0: if __name__ == .__main__.: ignore_errors = True [html] directory = coverage_html_report .. _config_run: [run] ----- These values are generally used when running product code, though some apply to more than one command. ``branch`` (boolean, default False): whether to measure :ref:`branch coverage ` in addition to statement coverage. ``cover_pylib`` (boolean, default False): whether to measure the Python standard library. ``concurrency`` (multi-string, default "thread"): the name concurrency libraries in use by the product code. If your program uses `multiprocessing`_, `gevent`_, `greenlet`_, or `eventlet`_, you must name that library in this option, or coverage.py will produce very wrong results. .. _multiprocessing: https://docs.python.org/3/library/multiprocessing.html .. _greenlet: https://greenlet.readthedocs.io/ .. _gevent: http://www.gevent.org/ .. _eventlet: http://eventlet.net/ Before version 4.2, this option only accepted a single string. .. versionadded:: 4.0 ``data_file`` (string, default ".coverage"): the name of the data file to use for storing or reporting coverage. This value can include a path to another directory. .. _config_run_disable_warnings: ``disable_warnings`` (multi-string): a list of warnings to disable. Warnings that can be disabled include a short string at the end, the name of the warning. See :ref:`cmd_warnings` for specific warnings. ``debug`` (multi-string): a list of debug options. See :ref:`the run --debug option ` for details. ``include`` (multi-string): a list of file name patterns, the files to include in measurement or reporting. Ignored if ``source`` is set. See :ref:`source` for details. ``note`` (string): an arbitrary string that will be written to the data file. You can use the :meth:`CoverageData.run_infos` method to retrieve this string from a data file. ``omit`` (multi-string): a list of file name patterns, the files to leave out of measurement or reporting. See :ref:`source` for details. ``parallel`` (boolean, default False): append the machine name, process id and random number to the data file name to simplify collecting data from many processes. See :ref:`cmd_combining` for more information. ``plugins`` (multi-string): a list of plugin package names. See :ref:`plugins` for more information. ``source`` (multi-string): a list of packages or directories, the source to measure during execution. If set, ``include`` is ignored. See :ref:`source` for details. ``timid`` (boolean, default False): use a simpler but slower trace method. This uses PyTracer instead of CTracer, and is only needed in very unusual circumstances. Try this if you get seemingly impossible results. .. _config_paths: [paths] ------- The entries in this section are lists of file paths that should be considered equivalent when combining data from different machines:: [paths] source = src/ /jenkins/build/*/src c:\myproj\src The names of the entries are ignored, you may choose any name that you like. The value is a list of strings. When combining data with the ``combine`` command, two file paths will be combined if they start with paths from the same list. The first value must be an actual file path on the machine where the reporting will happen, so that source code can be found. The other values can be file patterns to match against the paths of collected data, or they can be absolute or relative file paths on the current machine. See :ref:`cmd_combining` for more information. .. _config_report: [report] -------- Values common to many kinds of reporting. ``exclude_lines`` (multi-string): a list of regular expressions. Any line of your source code that matches one of these regexes is excluded from being reported as missing. More details are in :ref:`excluding`. If you use this option, you are replacing all the exclude regexes, so you'll need to also supply the "pragma: no cover" regex if you still want to use it. ``fail_under`` (float): a target coverage percentage. If the total coverage measurement is under this value, then exit with a status code of 2. If you specify a non-integral value, you must also set ``[report] precision`` properly to make use of the decimal places. A setting of 100 will fail any value under 100, regardless of the number of decimal places of precision. ``ignore_errors`` (boolean, default False): ignore source code that can't be found, emitting a warning instead of an exception. ``include`` (multi-string): a list of file name patterns, the files to include in reporting. See :ref:`source` for details. ``omit`` (multi-string): a list of file name patterns, the files to leave out of reporting. See :ref:`source` for details. ``partial_branches`` (multi-string): a list of regular expressions. Any line of code that matches one of these regexes is excused from being reported as a partial branch. More details are in :ref:`branch`. If you use this option, you are replacing all the partial branch regexes so you'll need to also supply the "pragma: no branch" regex if you still want to use it. ``precision`` (integer): the number of digits after the decimal point to display for reported coverage percentages. The default is 0, displaying for example "87%". A value of 2 will display percentages like "87.32%". This setting also affects the interpretation of the ``fail_under`` setting. ``show_missing`` (boolean, default False): when running a summary report, show missing lines. See :ref:`cmd_summary` for more information. ``skip_covered`` (boolean, default False): Don't include files in the report that are 100% covered files. See :ref:`cmd_summary` for more information. ``sort`` (string, default "Name"): Sort the text report by the named column. Allowed values are "Name", "Stmts", "Miss", "Branch", "BrPart", or "Cover". .. _config_html: [html] ------ Values particular to HTML reporting. The values in the ``[report]`` section also apply to HTML output, where appropriate. ``directory`` (string, default "htmlcov"): where to write the HTML report files. ``extra_css`` (string): the path to a file of CSS to apply to the HTML report. The file will be copied into the HTML output directory. Don't name it "style.css". This CSS is in addition to the CSS normally used, though you can overwrite as many of the rules as you like. ``title`` (string, default "Coverage report"): the title to use for the report. Note this is text, not HTML. .. _config_xml: [xml] ----- Values particular to XML reporting. The values in the ``[report]`` section also apply to XML output, where appropriate. ``output`` (string, default "coverage.xml"): where to write the XML report. ``package_depth`` (integer, default 99): controls which directories are identified as packages in the report. Directories deeper than this depth are not reported as packages. The default is that all directories are reported as packages. python-coverage-4.5+dfsg.1.orig/doc/contributing.rst0000644000076600000620000002372213177624110020474 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _contributing: =========================== Contributing to coverage.py =========================== .. :history: 20121112T154100, brand new docs. .. highlight:: console I welcome contributions to coverage.py. Over the years, dozens of people have provided patches of various sizes to add features or fix bugs. This page should have all the information you need to make a contribution. One source of history or ideas are the `bug reports`_ against coverage.py. There you can find ideas for requested features, or the remains of rejected ideas. .. _bug reports: https://bitbucket.org/ned/coveragepy/issues?status=new&status=open Before you begin ---------------- If you have an idea for coverage.py, run it by me before you begin writing code. This way, I can get you going in the right direction, or point you to previous work in the area. Things are not always as straightforward as they seem, and having the benefit of lessons learned by those before you can save you frustration. Getting the code ---------------- The coverage.py code is hosted on a `Mercurial`_ repository at https://bitbucket.org/ned/coveragepy. To get a working environment, follow these steps: #. (Optional, but recommended) Create a virtualenv to work in, and activate it. .. like this: mkvirtualenv -p /usr/local/pythonz/pythons/CPython-2.7.11/bin/python coverage #. Clone the repo:: $ hg clone https://bitbucket.org/ned/coveragepy $ cd coveragepy #. Install the requirements:: $ pip install -r requirements/dev.pip #. Install a number of versions of Python. Coverage.py supports a wide range of Python versions. The more you can test with, the more easily your code can be used as-is. If you only have one version, that's OK too, but may mean more work integrating your contribution. Running the tests ----------------- The tests are written as standard unittest-style tests, and are run with `tox`_:: $ tox py27 develop-inst-noop: /Users/ned/coverage/trunk py27 installed: apipkg==1.4,-e hg+ssh://hg@bitbucket.org/ned/coveragepy@6664140e34beddd6fee99b729bb9f4545a429c12#egg=coverage,covtestegg1==0.0.0,decorator==4.0.10,eventlet==0.19.0,execnet==1.4.1,funcsigs==1.0.2,gevent==1.1.2,greenlet==0.4.10,mock==2.0.0,pbr==1.10.0,py==1.4.31,PyContracts==1.7.12,pyparsing==2.1.10,pytest==3.0.5.dev0,pytest-warnings==0.2.0,pytest-xdist==1.15.0,six==1.10.0,unittest-mixins==1.1.1 py27 runtests: PYTHONHASHSEED='4113423111' py27 runtests: commands[0] | python setup.py --quiet clean develop no previously-included directories found matching 'tests/eggsrc/dist' no previously-included directories found matching 'tests/eggsrc/*.egg-info' py27 runtests: commands[1] | python igor.py zip_mods install_egg remove_extension py27 runtests: commands[2] | python igor.py test_with_tracer py === CPython 2.7.12 with Python tracer (.tox/py27/bin/python) === gw0 [679] / gw1 [679] / gw2 [679] scheduling tests via LoadScheduling ...........ss...................................................................................ss...s.......s...........................s...............................................................................s.....................................................................................................................................................s.........................................................................................s.s.s.s.s.ssssssssssss.ss..................................................s...................................................................s.............................................................................. 649 passed, 30 skipped in 42.89 seconds py27 runtests: commands[3] | python setup.py --quiet build_ext --inplace py27 runtests: commands[4] | python igor.py test_with_tracer c === CPython 2.7.12 with C tracer (.tox/py27/bin/python) === gw0 [679] / gw1 [679] / gw2 [679] scheduling tests via LoadScheduling ............ss................................................................................s..s.....s......s.........................s..........................................................................................s............................................................................................................s............................................................................................................................s...................................................................s........................................................................s............................................................................ 667 passed, 12 skipped in 41.87 seconds py35 develop-inst-noop: /Users/ned/coverage/trunk py35 installed: apipkg==1.4,-e hg+ssh://hg@bitbucket.org/ned/coveragepy@6664140e34beddd6fee99b729bb9f4545a429c12#egg=coverage,covtestegg1==0.0.0,decorator==4.0.10,eventlet==0.19.0,execnet==1.4.1,gevent==1.1.2,greenlet==0.4.10,mock==2.0.0,pbr==1.10.0,py==1.4.31,PyContracts==1.7.12,pyparsing==2.1.10,pytest==3.0.5.dev0,pytest-warnings==0.2.0,pytest-xdist==1.15.0,six==1.10.0,unittest-mixins==1.1.1 py35 runtests: PYTHONHASHSEED='4113423111' py35 runtests: commands[0] | python setup.py --quiet clean develop no previously-included directories found matching 'tests/eggsrc/dist' no previously-included directories found matching 'tests/eggsrc/*.egg-info' py35 runtests: commands[1] | python igor.py zip_mods install_egg remove_extension py35 runtests: commands[2] | python igor.py test_with_tracer py === CPython 3.5.2 with Python tracer (.tox/py35/bin/python) === gw0 [679] / gw1 [679] / gw2 [679] scheduling tests via LoadScheduling ............s..........................................................................................................................................................s..s...........................................................................................................................................................................................s.................................................................................................sssssssssssssssssss............................................................s................................................................s.............................................................................. 654 passed, 25 skipped in 47.25 seconds py35 runtests: commands[3] | python setup.py --quiet build_ext --inplace py35 runtests: commands[4] | python igor.py test_with_tracer c === CPython 3.5.2 with C tracer (.tox/py35/bin/python) === gw0 [679] / gw1 [679] / gw2 [679] scheduling tests via LoadScheduling ...........s...............................................................................................................................................................................................s......s..........................................................................................................................................................s.................................................................................................s....................................................................................................................................s.................................................................................. 673 passed, 6 skipped in 53.20 seconds _________________________________________________________________________________________ summary __________________________________________________________________________________________ py27: commands succeeded py35: commands succeeded Tox runs the complete test suite twice for each version of Python you have installed. The first run uses the Python implementation of the trace function, the second uses the C implementation. To limit tox to just a few versions of Python, use the ``-e`` switch:: $ tox -e py27,py33 To run just a few tests, you can use `pytest test selectors`_:: $ tox tests/test_misc.py $ tox tests/test_misc.py::SetupPyTest $ tox tests/test_misc.py::SetupPyTest::test_metadata These command run the tests in one file, one class, and just one test, respectively. Of course, run all the tests on every version of Python you have, before submitting a change. .. _pytest test selectors: http://doc.pytest.org/en/latest/usage.html#specifying-tests-selecting-tests Lint, etc --------- I try to keep the coverage.py as clean as possible. I use pylint to alert me to possible problems:: $ make lint pylint coverage setup.py tests python -m tabnanny coverage setup.py tests python igor.py check_eol The source is pylint-clean, even if it's because there are pragmas quieting some warnings. Please try to keep it that way, but don't let pylint warnings keep you from sending patches. I can clean them up. Lines should be kept to a 100-character maximum length. I recommend an `editorconfig.org`_ plugin for your editor of choice. Other style questions are best answered by looking at the existing code. Formatting of docstrings, comments, long lines, and so on, should match the code that already exists. Coverage testing coverage.py ---------------------------- Coverage.py can measure itself, but it's complicated. The process has been packaged up to make it easier:: $ make metacov metahtml Then look at htmlcov/index.html. Note that due to the recursive nature of coverage.py measuring itself, there are some parts of the code that will never appear as covered, even though they are executed. Contributing ------------ When you are ready to contribute a change, any way you can get it to me is probably fine. A pull request on Bitbucket is great, but a simple diff or patch works too. .. _editorconfig.org: http://editorconfig.org .. _Mercurial: https://www.mercurial-scm.org/ .. _tox: https://tox.readthedocs.io/ python-coverage-4.5+dfsg.1.orig/doc/changes.rst0000644000076600000620000000033613173515667017405 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _changes: .. module:: coverage .. include:: ../CHANGES.rst python-coverage-4.5+dfsg.1.orig/doc/cmd.rst0000644000076600000620000004471213224244721016531 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _cmd: ============================== Coverage.py command line usage ============================== .. :history: 20090524T134300, brand new docs. .. :history: 20090613T164000, final touches for 3.0 .. :history: 20090913T084400, new command line syntax .. :history: 20091004T170700, changes for 3.1 .. :history: 20091127T200700, changes for 3.2 .. :history: 20100223T200600, changes for 3.3 .. :history: 20100725T211700, updated for 3.4 .. :history: 20110827T212500, updated for 3.5.1, combining aliases .. :history: 20120119T075600, Added some clarification from George Paci .. :history: 20120504T091800, Added info about execution warnings, and 3.5.2. .. :history: 20120807T211600, Clarified the combine rules. .. :history: 20121003T074600, Fixed an option reference, https://bitbucket.org/ned/coveragepy/issue/200/documentation-mentions-output-xml-instead .. :history: 20121117T091000, Added command aliases. .. :history: 20140924T193000, Added --concurrency .. :history: 20150802T174700, Updated for 4.0b1 .. highlight:: console When you install coverage.py, a command-line script simply called ``coverage`` is placed in your Python scripts directory. To help with multi-version installs, it will also create either a ``coverage2`` or ``coverage3`` alias, and a ``coverage-X.Y`` alias, depending on the version of Python you're using. For example, when installing on Python 2.7, you will be able to use ``coverage``, ``coverage2``, or ``coverage-2.7`` on the command line. Coverage.py has a number of commands which determine the action performed: * **run** -- Run a Python program and collect execution data. * **report** -- Report coverage results. * **html** -- Produce annotated HTML listings with coverage results. * **xml** -- Produce an XML report with coverage results. * **annotate** -- Annotate source files with coverage results. * **erase** -- Erase previously collected coverage data. * **combine** -- Combine together a number of data files. * **debug** -- Get diagnostic information. Help is available with the **help** command, or with the ``--help`` switch on any other command:: $ coverage help $ coverage help run $ coverage run --help Version information for coverage.py can be displayed with ``coverage --version``. Any command can use a configuration file by specifying it with the ``--rcfile=FILE`` command-line switch. Any option you can set on the command line can also be set in the configuration file. This can be a better way to control coverage.py since the configuration file can be checked into source control, and can provide options that other invocation techniques (like test runner plugins) may not offer. See :ref:`config` for more details. .. _cmd_execution: Execution --------- You collect execution data by running your Python program with the **run** command:: $ coverage run my_program.py arg1 arg2 blah blah ..your program's output.. blah blah Your program runs just as if it had been invoked with the Python command line. Arguments after your file name are passed to your program as usual in ``sys.argv``. Rather than providing a file name, you can use the ``-m`` switch and specify an importable module name instead, just as you can with the Python ``-m`` switch:: $ coverage run -m packagename.modulename arg1 arg2 blah blah ..your program's output.. blah blah If you want :ref:`branch coverage ` measurement, use the ``--branch`` flag. Otherwise only statement coverage is measured. You can specify the code to measure with the ``--source``, ``--include``, and ``--omit`` switches. See :ref:`Specifying source files ` for details of their interpretation. Remember to put options for run after "run", but before the program invocation:: $ coverage run --source=dir1,dir2 my_program.py arg1 arg2 $ coverage run --source=dir1,dir2 -m packagename.modulename arg1 arg2 Coverage.py can measure multi-threaded programs by default. If you are using more exotic concurrency, with the `multiprocessing`_, `greenlet`_, `eventlet`_, or `gevent`_ libraries, then coverage.py will get very confused. Use the ``--concurrency`` switch to properly measure programs using these libraries. Give it a value of ``multiprocessing``, ``thread``, ``greenlet``, ``eventlet``, or ``gevent``. Values other than ``thread`` require the :ref:`C extension `. If you are using ``--concurrency=multiprocessing``, you must set other options in the configuration file. Options on the command line will not be passed to the processes that multiprocessing creates. Best practice is to use the configuration file for all options. .. _multiprocessing: https://docs.python.org/3/library/multiprocessing.html .. _greenlet: https://greenlet.readthedocs.io/ .. _gevent: http://www.gevent.org/ .. _eventlet: http://eventlet.net/ By default, coverage.py does not measure code installed with the Python interpreter, for example, the standard library. If you want to measure that code as well as your own, add the ``-L`` (or ``--pylib``) flag. If your coverage results seem to be overlooking code that you know has been executed, try running coverage.py again with the ``--timid`` flag. This uses a simpler but slower trace method. Projects that use DecoratorTools, including TurboGears, will need to use ``--timid`` to get correct results. If you are measuring coverage in a multi-process program, or across a number of machines, you'll want the ``--parallel-mode`` switch to keep the data separate during measurement. See :ref:`cmd_combining` below. .. _cmd_warnings: Warnings -------- During execution, coverage.py may warn you about conditions it detects that could affect the measurement process. The possible warnings include: * "Trace function changed, measurement is likely wrong: XXX (trace-changed)" Coverage measurement depends on a Python setting called the trace function. Other Python code in your product might change that function, which will disrupt coverage.py's measurement. This warning indicates that has happened. The XXX in the message is the new trace function value, which might provide a clue to the cause. * "Module XXX has no Python source (module-not-python)" You asked coverage.py to measure module XXX, but once it was imported, it turned out not to have a corresponding .py file. Without a .py file, coverage.py can't report on missing lines. * "Module XXX was never imported (module-not-imported)" You asked coverage.py to measure module XXX, but it was never imported by your program. * "No data was collected (no-data-collected)" Coverage.py ran your program, but didn't measure any lines as executed. This could be because you asked to measure only modules that never ran, or for other reasons. * "Module XXX was previously imported, but not measured (module-not-measured)" You asked coverage.py to measure module XXX, but it had already been imported when coverage started. This meant coverage.py couldn't monitor its execution. * "--include is ignored because --source is set (include-ignored)" Both ``--include`` and ``--source`` were specified while running code. Both are meant to focus measurement on a particular part of your source code, so ``--include`` is ignored in favor of ``--source``. Individual warnings can be disabled with the `disable_warnings `_ configuration setting. To silence "No data was collected," add this to your .coveragerc file:: [run] disable_warnings = no-data-collected .. _cmd_datafile: Data file --------- Coverage.py collects execution data in a file called ".coverage". If need be, you can set a new file name with the COVERAGE_FILE environment variable. This can include a path to another directory. By default, each run of your program starts with an empty data set. If you need to run your program multiple times to get complete data (for example, because you need to supply disjoint options), you can accumulate data across runs with the ``-a`` flag on the **run** command. To erase the collected data, use the **erase** command:: $ coverage erase .. _cmd_combining: Combining data files -------------------- If you need to collect coverage data from different machines or processes, coverage.py can combine multiple files into one for reporting. Once you have created a number of these files, you can copy them all to a single directory, and use the **combine** command to combine them into one .coverage data file:: $ coverage combine You can also name directories or files on the command line:: $ coverage combine data1.dat windows_data_files/ Coverage.py will collect the data from those places and combine them. The current directory isn't searched if you use command-line arguments. If you also want data from the current directory, name it explicitly on the command line. When coverage.py looks in directories for data files to combine, even the current directory, it only reads files with certain names. It looks for files named the same as the data file (defaulting to ".coverage"), with a dotted suffix. Here are some examples of data files that can be combined:: .coverage.machine1 .coverage.20120807T212300 .coverage.last_good_run.ok An existing combined data file is ignored and re-written. If you want to use **combine** to accumulate results into the .coverage data file over a number of runs, use the ``--append`` switch on the **combine** command. This behavior was the default before version 4.2. The ``run --parallel-mode`` switch automatically creates separate data files for each run which can be combined later. The file names include the machine name, the process id, and a random number:: .coverage.Neds-MacBook-Pro.local.88335.316857 .coverage.Geometer.8044.799674 If the different machines run your code from different places in their file systems, coverage.py won't know how to combine the data. You can tell coverage.py how the different locations correlate with a ``[paths]`` section in your configuration file. See :ref:`config_paths` for details. If any data files can't be read, coverage.py will print a warning indicating the file and the problem. .. _cmd_reporting: Reporting --------- Coverage.py provides a few styles of reporting, with the **report**, **html**, **annotate**, and **xml** commands. They share a number of common options. The command-line arguments are module or file names to report on, if you'd like to report on a subset of the data collected. The ``--include`` and ``--omit`` flags specify lists of file name patterns. They control which files to report on, and are described in more detail in :ref:`source`. The ``-i`` or ``--ignore-errors`` switch tells coverage.py to ignore problems encountered trying to find source files to report on. This can be useful if some files are missing, or if your Python execution is tricky enough that file names are synthesized without real source files. If you provide a ``--fail-under`` value, the total percentage covered will be compared to that value. If it is less, the command will exit with a status code of 2, indicating that the total coverage was less than your target. This can be used as part of a pass/fail condition, for example in a continuous integration server. This option isn't available for **annotate**. .. _cmd_summary: Coverage summary ---------------- The simplest reporting is a textual summary produced with **report**:: $ coverage report Name Stmts Miss Cover --------------------------------------------- my_program.py 20 4 80% my_module.py 15 2 86% my_other_module.py 56 6 89% --------------------------------------------- TOTAL 91 12 87% For each module executed, the report shows the count of executable statements, the number of those statements missed, and the resulting coverage, expressed as a percentage. The ``-m`` flag also shows the line numbers of missing statements:: $ coverage report -m Name Stmts Miss Cover Missing ------------------------------------------------------- my_program.py 20 4 80% 33-35, 39 my_module.py 15 2 86% 8, 12 my_other_module.py 56 6 89% 17-23 ------------------------------------------------------- TOTAL 91 12 87% If you are using branch coverage, then branch statistics will be reported in the Branch and BrPart (for Partial Branch) columns, the Missing column will detail the missed branches:: $ coverage report -m Name Stmts Miss Branch BrPart Cover Missing --------------------------------------------------------------------- my_program.py 20 4 10 2 80% 33-35, 36->38, 39 my_module.py 15 2 3 0 86% 8, 12 my_other_module.py 56 6 5 1 89% 17-23, 40->45 --------------------------------------------------------------------- TOTAL 91 12 18 3 87% You can restrict the report to only certain files by naming them on the command line:: $ coverage report -m my_program.py my_other_module.py Name Stmts Miss Cover Missing ------------------------------------------------------- my_program.py 20 4 80% 33-35, 39 my_other_module.py 56 6 89% 17-23 ------------------------------------------------------- TOTAL 76 10 87% The ``--skip-covered`` switch will leave out any file with 100% coverage, letting you focus on the files that still need attention. Other common reporting options are described above in :ref:`cmd_reporting`. .. _cmd_html: HTML annotation --------------- Coverage.py can annotate your source code for which lines were executed and which were not. The **html** command creates an HTML report similar to the **report** summary, but as an HTML file. Each module name links to the source file decorated to show the status of each line. Here's a `sample report`__. __ https://nedbatchelder.com/files/sample_coverage_html/index.html Lines are highlighted green for executed, red for missing, and gray for excluded. The counts at the top of the file are buttons to turn on and off the highlighting. A number of keyboard shortcuts are available for navigating the report. Click the keyboard icon in the upper right to see the complete list. The title of the report can be set with the ``title`` setting in the ``[html]`` section of the configuration file, or the ``--title`` switch on the command line. If you prefer a different style for your HTML report, you can provide your own CSS file to apply, by specifying a CSS file in the ``[html]`` section of the configuration file. See :ref:`config_html` for details. The ``-d`` argument specifies an output directory, defaulting to "htmlcov":: $ coverage html -d coverage_html Other common reporting options are described above in :ref:`cmd_reporting`. Generating the HTML report can be time-consuming. Stored with the HTML report is a data file that is used to speed up reporting the next time. If you generate a new report into the same directory, coverage.py will skip generating unchanged pages, making the process faster. The ``--skip-covered`` switch will leave out any file with 100% coverage, letting you focus on the files that still need attention. .. _cmd_annotation: Text annotation --------------- The **annotate** command produces a text annotation of your source code. With a ``-d`` argument specifying an output directory, each Python file becomes a text file in that directory. Without ``-d``, the files are written into the same directories as the original Python files. Coverage status for each line of source is indicated with a character prefix:: > executed ! missing (not executed) - excluded For example:: # A simple function, never called with x==1 > def h(x): """Silly function.""" - if 0: #pragma: no cover - pass > if x == 1: ! a = 1 > else: > a = 2 Other common reporting options are described above in :ref:`cmd_reporting`. .. _cmd_xml: XML reporting ------------- The **xml** command writes coverage data to a "coverage.xml" file in a format compatible with `Cobertura`_. .. _Cobertura: http://cobertura.github.io/cobertura/ You can specify the name of the output file with the ``-o`` switch. Other common reporting options are described above in :ref:`cmd_reporting`. .. _cmd_debug: Diagnostics ----------- The **debug** command shows internal information to help diagnose problems. If you are reporting a bug about coverage.py, including the output of this command can often help:: $ coverage debug sys > please_attach_to_bug_report.txt Three types of information are available: * ``config``: show coverage's configuration * ``sys``: show system configuration, * ``data``: show a summary of the collected coverage data .. _cmd_run_debug: The ``--debug`` option is available on all commands. It instructs coverage.py to log internal details of its operation, to help with diagnosing problems. It takes a comma-separated list of options, each indicating a facet of operation to log: * ``callers``: annotate each debug message with a stack trace of the callers to that point. * ``config``: before starting, dump all the :ref:`configuration ` values. * ``dataio``: log when reading or writing any data file. * ``dataop``: log when data is added to the CoverageData object. * ``multiproc``: log the start and stop of multiprocessing processes. * ``pid``: annotate all warnings and debug output with the process id. * ``plugin``: print information about plugin operations. * ``process``: show process creation information, and changes in the current directory. * ``sys``: before starting, dump all the system and environment information, as with :ref:`coverage debug sys `. * ``trace``: print every decision about whether to trace a file or not. For files not being traced, the reason is also given. Debug options can also be set with the ``COVERAGE_DEBUG`` environment variable, a comma-separated list of these options. The debug output goes to stderr, unless the ``COVERAGE_DEBUG_FILE`` environment variable names a different file, which will be appended to. python-coverage-4.5+dfsg.1.orig/doc/python-coverage.1.txt0000644000076600000620000001371113146037571021247 0ustar staff=============== python-coverage =============== ------------------------------------------------- measure code coverage of Python program execution ------------------------------------------------- :Author: Ned Batchelder :Author: |author| :Date: 2015-09-20 :Copyright: Apache 2.0 license, attribution and disclaimer required. :Manual section: 1 :Manual group: Coverage.py .. |command| replace:: **python-coverage** .. To test this file: $ rst2man < doc/python-coverage.1.txt | groff -man -Tascii SYNOPSIS ======== | |command| `command` [ `option` ... ] | |command| **help** [ `command` ] DESCRIPTION =========== |command| executes a Python program and measures which of its statements are executed and which are not, and reports these coverage measurements. COMMAND OVERVIEW ================ |command| **annotate** Annotate source files with execution information. |command| **combine** Combine a number of data files. |command| **erase** Erase previously collected coverage data. |command| **help** Get help on using coverage.py. |command| **html** Create an HTML report. |command| **report** Report coverage stats on modules. |command| **run** Run a Python program and measure code execution. |command| **xml** Create an XML report of coverage results. GLOBAL OPTIONS ============== **--help**, **-h** Describe how to use coverage.py, in general or a command. **--rcfile** `RCFILE` Specify configuration file `RCFILE`. Defaults to ``.coveragerc``. **--omit** `PATTERN` [ , ... ] Omit files when their file name matches one of these PATTERNs. Usually needs quoting on the command line. **--include** `PATTERN` [ , ... ] Include only files whose paths match one of these PATTERNs. Accepts shell-style wildcards, which must be quoted. **--debug** `DEBUGOPT`,... Debug options `DEBUGOPT`, separated by commas. COMMAND REFERENCE ================= **annotate** [ `option` ... ] Options: \-d `DIR`, --directory=`DIR` Write the output files to DIR. \-i, --ignore-errors Ignore errors while reading source files. **combine** [ `option` ... ] [ `PATH` ... ] Combine data from multiple coverage files collected with ``run -p``. The combined results are written to a single file representing the union of the data. If `PATH` is specified, they are files or directories containing data to be combined. Options: \--append Append coverage data to .coverage, otherwise it starts clean each time. **erase** Erase previously collected coverage data. **help** [ `command` ] Describe how to use coverage.py. **html** [ `option` ... ] [ `MODULE` ... ] Create an HTML report of the coverage of each `MODULE` file. Each file gets its own page, with the source decorated to show executed, excluded, and missed lines. Options: \-d `DIR`, --directory `DIR` Write the output files to `DIR`. \--fail-under `MIN` Exit with a status of 2 if the total coverage is less than `MIN`. \-i, --ignore-errors Ignore errors while reading source files. \--skip-covered Skip files with 100% coverage. \--title `TITLE` Use the text string `TITLE` as the title on the HTML. **report** [ `option` ... ] [ `MODULE` ... ] Report coverage statistics on each `MODULE`. Options: \--fail-under `MIN` Exit with a status of 2 if the total coverage is less than `MIN`. \-i, --ignore-errors Ignore errors while reading source files. \-m, --show-missing Show line numbers of statements in each module that weren't executed. \--skip-covered Skip files with 100% coverage. **run** [ `options` ... ] `PROGRAMFILE` [ `program_options` ] Run a Python program `PROGRAMFILE`, measuring code execution. Options: \-a, --append Append coverage data to .coverage, otherwise it is started clean with each run. \--branch Measure branch coverage in addition to statement coverage. \--concurrency `LIB` Properly measure code using a concurrency library. Valid values are: thread, gevent, greenlet, eventlet, multiprocessing. \-L, --pylib Measure coverage even inside the Python installed library, which isn't done by default. \-p, --parallel-mode Append the machine name, process id and random number to the ``.coverage`` data file name to simplify collecting data from many processes. \--source `SOURCE` ... A list of packages or directories of code to be measured. \--timid Use a simpler but slower trace method. Try this if you get seemingly impossible results! **xml** [ `options` ... ] [ `MODULES` ... ] Generate an XML report of coverage results on each `MODULE`. Options: \--fail-under `MIN` Exit with a status of 2 if the total coverage is less than `MIN`. \-i, --ignore-errors Ignore errors while reading source files. \-o `OUTFILE` Write the XML report to `OUTFILE`. Defaults to ``coverage.xml``. ENVIRONMENT VARIABLES ===================== COVERAGE_FILE Path to the file where coverage measurements are collected to and reported from. Default: ``.coverage`` in the current working directory. HISTORY ======= The |command| command is a Python program which calls the ``coverage`` Python library to do all the work. The library was originally developed by Gareth Rees, and is now developed by Ned Batchelder and many others. This manual page was written by |author|. .. |author| replace:: |authorname| |authoremail| .. |authorname| replace:: Ben Finney .. |authoremail| replace:: .. Local variables: mode: rst coding: utf-8 time-stamp-format: "%:y-%02m-%02d" time-stamp-start: "^:Date:[ ]+" time-stamp-end: "$" time-stamp-line-limit: 20 End: vim: filetype=rst fileencoding=utf-8 : python-coverage-4.5+dfsg.1.orig/doc/api_plugin.rst0000644000076600000620000000130113224176244020104 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _api_plugin: =============== Plug-in classes =============== .. :history: 20150815T132400, new doc for 4.0b2 .. versionadded:: 4.0 .. automodule:: coverage.plugin .. module:: coverage The CoveragePlugin class ------------------------ .. autoclass:: CoveragePlugin :members: :member-order: bysource The FileTracer class -------------------- .. autoclass:: FileTracer :members: :member-order: bysource The FileReporter class ---------------------- .. autoclass:: FileReporter :members: :member-order: bysource python-coverage-4.5+dfsg.1.orig/doc/source.rst0000644000076600000620000000747113177624110017270 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _source: ======================= Specifying source files ======================= .. :history: 20100725T172000, new in 3.4 When coverage.py is running your program and measuring its execution, it needs to know what code to measure and what code not to. Measurement imposes a speed penalty, and the collected data must be stored in memory and then on disk. More importantly, when reviewing your coverage reports, you don't want to be distracted with modules that aren't your concern. Coverage.py has a number of ways you can focus it in on the code you care about. .. _source_execution: Execution --------- When running your code, the ``coverage run`` command will by default measure all code, unless it is part of the Python standard library. You can specify source to measure with the ``--source`` command-line switch, or the ``[run] source`` configuration value. The value is a comma- or newline-separated list of directories or package names. If specified, only source inside these directories or packages will be measured. Specifying the source option also enables coverage.py to report on unexecuted files, since it can search the source tree for files that haven't been measured at all. Only importable files (ones at the root of the tree, or in directories with a ``__init__.py`` file) will be considered. Files with unusual punctuation in their names will be skipped (they are assumed to be scratch files written by text editors). Files that do not end with ``.py`` or ``.pyo`` or ``.pyc`` will also be skipped. You can further fine-tune coverage.py's attention with the ``--include`` and ``--omit`` switches (or ``[run] include`` and ``[run] omit`` configuration values). ``--include`` is a list of file name patterns. If specified, only files matching those patterns will be measured. ``--omit`` is also a list of file name patterns, specifying files not to measure. If both ``include`` and ``omit`` are specified, first the set of files is reduced to only those that match the include patterns, then any files that match the omit pattern are removed from the set. The ``include`` and ``omit`` file name patterns follow typical shell syntax: ``*`` matches any number of characters and ``?`` matches a single character. Patterns that start with a wildcard character are used as-is, other patterns are interpreted relative to the current directory:: [run] omit = # omit anything in a .local directory anywhere */.local/* # omit everything in /usr /usr/* # omit this single file utils/tirefire.py The ``source``, ``include``, and ``omit`` values all work together to determine the source that will be measured. If both ``source`` and ``include`` are set, the ``include`` value is ignored and a warning is printed on the standard output. .. _source_reporting: Reporting --------- Once your program is measured, you can specify the source files you want reported. Usually you want to see all the code that was measured, but if you are measuring a large project, you may want to get reports for just certain parts. The report commands (``report``, ``html``, ``annotate``, and ``xml``) all take optional ``modules`` arguments, and ``--include`` and ``--omit`` switches. The ``modules`` arguments specify particular modules to report on. The ``include`` and ``omit`` values are lists of file name patterns, just as with the ``run`` command. Remember that the reporting commands can only report on the data that has been collected, so the data you're looking for may not be in the data available for reporting. Note that these are ways of specifying files to measure. You can also exclude individual source lines. See :ref:`excluding` for details. python-coverage-4.5+dfsg.1.orig/doc/api_coverage.rst0000644000076600000620000000116513146037571020413 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _api_coverage: The Coverage class ------------------ .. :history: 20150802T174800, new file for 4.0b1 .. module:: coverage .. autoclass:: Coverage :members: :exclude-members: use_cache, sys_info :special-members: __init__ Starting coverage.py automatically ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This function is used to start coverage measurement automatically when Python starts. See :ref:`subprocess` for details. .. autofunction:: process_startup python-coverage-4.5+dfsg.1.orig/doc/plugins.rst0000644000076600000620000000455313225547150017451 0ustar staff.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 .. For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt .. _plugins: ======== Plug-ins ======== .. :history: 20150124T143000, new page. .. :history: 20150802T174600, updated for 4.0b1 .. :history: 20171228T213800, updated for configurer plugins Coverage.py's behavior can be extended with third-party plug-ins. A plug-in is a separately installed Python class that you register in your .coveragerc. Plugins can alter a number of aspects of coverage.py's behavior, including implementing coverage measurement for non-Python files. Information about using plug-ins is on this page. To write a plug-in, see :ref:`api_plugin`. .. versionadded:: 4.0 Using plug-ins -------------- To use a coverage.py plug-in, you install it and configure it. For this example, let's say there's a Python package called ``something`` that provides a coverage.py plug-in called ``something.plugin``. #. Install the plug-in's package as you would any other Python package:: pip install something #. Configure coverage.py to use the plug-in. You do this by editing (or creating) your .coveragerc file, as described in :ref:`config`. The ``plugins`` setting indicates your plug-in. It's a list of importable module names of plug-ins:: [run] plugins = something.plugin #. If the plug-in needs its own configuration, you can add those settings in the .coveragerc file in a section named for the plug-in:: [something.plugin] option1 = True option2 = abc.foo Check the documentation for the plug-in for details on the options it takes. #. Run your tests with coverage.py as you usually would. If you get a message like "Plugin file tracers (something.plugin) aren't supported with PyTracer," then you don't have the :ref:`C extension ` installed. The C extension is needed for certain plug-ins. Available plug-ins ------------------ Some coverage.py plug-ins you might find useful: * `Django template coverage.py plug-in`__: for measuring coverage in Django templates. .. __: https://pypi.python.org/pypi/django_coverage_plugin * `Mako template coverage plug-in`__: for measuring coverage in Mako templates. Doesn't work yet, probably needs some changes in Mako itself. .. __: https://bitbucket.org/ned/coverage-mako-plugin python-coverage-4.5+dfsg.1.orig/doc/_static/0000755000076600000620000000000013235414514016653 5ustar staffpython-coverage-4.5+dfsg.1.orig/doc/_static/coverage.css0000644000076600000620000000013313146037571021162 0ustar staffbody { font-family: Georgia; } h1, h2, h3, h4, h5, h6 { font-family: Helvetica; } python-coverage-4.5+dfsg.1.orig/__main__.py0000644000076600000620000000134213173515667016566 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Be able to execute coverage.py by pointing Python at a working tree.""" import runpy import os PKG = 'coverage' try: run_globals = runpy.run_module(PKG, run_name='__main__', alter_sys=True) executed = os.path.splitext(os.path.basename(run_globals['__file__']))[0] if executed != '__main__': # For Python 2.5 compatibility raise ImportError( 'Incorrectly executed %s instead of __main__' % executed ) except ImportError: # For Python 2.6 compatibility runpy.run_module('%s.__main__' % PKG, run_name='__main__', alter_sys=True) python-coverage-4.5+dfsg.1.orig/Makefile0000644000076600000620000000656313177624110016132 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Makefile for utility work on coverage.py. default: @echo "* No default action *" clean: -pip uninstall -y coverage -rm -f *.pyd */*.pyd -rm -f *.so */*.so -PYTHONPATH=. python tests/test_farm.py clean -rm -rf tests/farm/*/out -rm -rf build coverage.egg-info dist htmlcov -rm -f *.pyc */*.pyc */*/*.pyc */*/*/*.pyc */*/*/*/*.pyc */*/*/*/*/*.pyc -rm -f *.pyo */*.pyo */*/*.pyo */*/*/*.pyo */*/*/*/*.pyo */*/*/*/*/*.pyo -rm -f *.bak */*.bak */*/*.bak */*/*/*.bak */*/*/*/*.bak */*/*/*/*/*.bak -rm -f *$$py.class */*$$py.class */*/*$$py.class */*/*/*$$py.class */*/*/*/*$$py.class */*/*/*/*/*$$py.class -rm -rf __pycache__ */__pycache__ */*/__pycache__ */*/*/__pycache__ */*/*/*/__pycache__ */*/*/*/*/__pycache__ -rm -f coverage/*,cover -rm -f MANIFEST -rm -f .coverage .coverage.* coverage.xml .metacov* -rm -f tests/zipmods.zip -rm -rf tests/eggsrc/build tests/eggsrc/dist tests/eggsrc/*.egg-info -rm -f setuptools-*.egg distribute-*.egg distribute-*.tar.gz -rm -rf doc/_build doc/_spell doc/sample_html_beta -rm -rf .tox_kits sterile: clean -rm -rf .tox* LINTABLE = coverage tests igor.py setup.py __main__.py lint: tox -e lint todo: -grep -R --include=*.py TODO $(LINTABLE) spell: -pylint --disable=all --enable=spelling $(LINTABLE) pep8: pycodestyle --filename=*.py --repeat $(LINTABLE) test: tox -e py27,py35 $(ARGS) TOX_SMOKE_ARGS = -n 6 -m "not expensive" --maxfail=3 $(ARGS) smoke: COVERAGE_NO_PYTRACER=1 tox -e py26,py33 -- $(TOX_SMOKE_ARGS) pysmoke: COVERAGE_NO_CTRACER=1 tox -e py26,py33 -- $(TOX_SMOKE_ARGS) metacov: COVERAGE_COVERAGE=yes tox $(ARGS) metahtml: python igor.py combine_html # Kitting kit: python setup.py sdist --formats=gztar wheel: tox -c tox_wheels.ini $(ARGS) manylinux: docker run -it --init --rm -v `pwd`:/io quay.io/pypa/manylinux1_x86_64 /io/ci/manylinux.sh build docker run -it --init --rm -v `pwd`:/io quay.io/pypa/manylinux1_i686 /io/ci/manylinux.sh build kit_upload: twine upload dist/* kit_local: # pip.conf looks like this: # [global] # find-links = file:///Users/ned/Downloads/local_pypi cp -v dist/* `awk -F "//" '/find-links/ {print $$2}' ~/.pip/pip.conf` # pip caches wheels of things it has installed. Clean them out so we # don't go crazy trying to figure out why our new code isn't installing. find ~/Library/Caches/pip/wheels -name 'coverage-*' -delete download_appveyor: python ci/download_appveyor.py nedbat/coveragepy build_ext: python setup.py build_ext install: python setup.py install uninstall: -rm -rf $(PYHOME)/lib/site-packages/coverage* -rm -rf $(PYHOME)/scripts/coverage* # Documentation SPHINXBUILD = sphinx-build SPHINXOPTS = -a -E doc WEBHOME = ~/web/stellated/ WEBSAMPLE = $(WEBHOME)/files/sample_coverage_html WEBSAMPLEBETA = $(WEBHOME)/files/sample_coverage_html_beta docreqs: pip install -r doc/requirements.pip dochtml: PYTHONPATH=$(CURDIR) $(SPHINXBUILD) -b html $(SPHINXOPTS) doc/_build/html @echo @echo "Build finished. The HTML pages are in doc/_build/html." docspell: $(SPHINXBUILD) -b spelling $(SPHINXOPTS) doc/_spell publish: rm -f $(WEBSAMPLE)/*.* mkdir -p $(WEBSAMPLE) cp doc/sample_html/*.* $(WEBSAMPLE) publishbeta: rm -f $(WEBSAMPLEBETA)/*.* mkdir -p $(WEBSAMPLEBETA) cp doc/sample_html_beta/*.* $(WEBSAMPLEBETA) python-coverage-4.5+dfsg.1.orig/requirements/0000755000076600000620000000000013235414514017203 5ustar staffpython-coverage-4.5+dfsg.1.orig/requirements/dev.pip0000644000076600000620000000077413234610644020504 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Requirements for doing local development work on coverage.py. # https://requires.io/github/nedbat/coveragepy/requirements/ # PyPI requirements for running tests. -r tox.pip -r pytest.pip # for linting. greenlet==0.4.13 mock==2.0.0 PyContracts==1.8.2 pyenchant==2.0.0 pylint==1.8.2 unittest-mixins==1.4 # for kitting. requests==2.18.4 twine==1.9.1 python-coverage-4.5+dfsg.1.orig/requirements/wheel.pip0000644000076600000620000000021113177624110021013 0ustar staff# Things needed to make wheels for coverage.py setuptools==35.0.2 # We need to stick with 0.29.0 until we drop 2.6 and 3.3 wheel==0.29.0 python-coverage-4.5+dfsg.1.orig/requirements/ci.pip0000644000076600000620000000037113217605374020317 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # Things CI servers need to succeeed. -r tox.pip -r pytest.pip -r wheel.pip tox-travis==0.10 python-coverage-4.5+dfsg.1.orig/requirements/tox.pip0000644000076600000620000000017113217605566020537 0ustar staff# The version of tox used by coverage.py tox==2.9.1 # Adds env recreation on requirements file changes. tox-battery==0.5 python-coverage-4.5+dfsg.1.orig/requirements/pytest.pip0000644000076600000620000000047113220714357021251 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # The pytest specifics used by coverage.py # Keep pytest at 3.2.x until we are done with Python 2.6 and 3.3 pytest==3.2.5 pytest-xdist==1.20.1 flaky==3.4.0 python-coverage-4.5+dfsg.1.orig/howto.txt0000644000076600000620000000725213235412545016371 0ustar staff* Release checklist - Version number in coverage/version.py version_info = (4, 0, 2, 'alpha', 1) version_info = (4, 0, 2, 'beta', 1) version_info = (4, 0, 2, 'candidate', 1) version_info = (4, 0, 2, 'final', 0) - Python version number in classifiers in setup.py - Copyright date in NOTICE.txt - Update CHANGES.rst, including release date. - Update README.rst - "New in x.y:" - Update docs - Version, date and python versions in doc/index.rst - Version and copyright date in doc/conf.py - Look for CHANGEME comments - Don't forget the man page: doc/python-coverage.1.txt - Check that the docs build correctly: $ tox -e doc - Done with changes to source files, check them in. - hg push - Generate new sample_html to get the latest, incl footer version number: make clean pip install -e . pip install nose cd ~/cog/trunk rm -rf htmlcov coverage run --branch --source=cogapp -m nose cogapp/test_cogapp.py:CogTestsInMemory coverage combine coverage html - IF BETA: rm -f ~/coverage/trunk/doc/sample_html_beta/*.* cp -r htmlcov/ ~/coverage/trunk/doc/sample_html_beta/ - IF NOT BETA: rm -f ~/coverage/trunk/doc/sample_html/*.* cp -r htmlcov/ ~/coverage/trunk/doc/sample_html/ cd ~/coverage/trunk - IF NOT BETA: check in the new sample html - Build and publish docs: - IF BETA: $ make publishbeta - ELSE: $ make publish - Kits: - Start fresh: - $ make clean - Source kit and wheels: - $ make kit wheel - Linux wheels: - $ make manylinux - Windows kits - wait for over an hour for Appveyor to build kits. - https://ci.appveyor.com/project/nedbat/coveragepy - $ make download_appveyor - examine the dist directory, and remove anything that looks malformed. - Update PyPi: - upload kits: - $ make kit_upload - Visit https://pypi.python.org/pypi?:action=pkg_edit&name=coverage : - show/hide the proper versions. - Tag the tree - hg tag -m "Coverage 3.0.1" coverage-3.0.1 - Update nedbatchelder.com - Blog post? - Update readthedocs - visit https://readthedocs.org/projects/coverage/versions/ - find the latest tag in the inactive list, edit it, make it active. - IF NOT BETA: - visit https://readthedocs.org/dashboard/coverage/versions/ - change the default version to the new version - Update bitbucket: - Issue tracker should get new version number in picker. # Note: don't delete old version numbers: it marks changes on the tickets # with that number. - Announce on coveragepy-announce@googlegroups.com . - Announce on TIP. - Bump version: - coverage/version.py - increment version number - IF NOT BETA: - set to alpha-0 if just released - CHANGES.rst - add an "Unreleased" section to the top. * Testing - Testing of Python code is handled by tox. - Create and activate a virtualenv - pip install -r requirements/dev.pip - $ tox - Testing on Linux: - $ docker run -it --init --rm -v `pwd`:/io quay.io/pypa/manylinux1_x86_64 /io/ci/manylinux.sh test - For complete coverage testing: $ make metacov This will run coverage.py under its own measurement. You can do this in different environments (Linux vs. Windows, for example), then copy the data files (.metacov.*) to one machine for combination and reporting. To combine and report: $ make metahtml - To run the Javascript tests: open tests/js/index.html in variety of browsers. python-coverage-4.5+dfsg.1.orig/pylintrc0000644000076600000620000002273313217552400016253 0ustar staff# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt # lint Python modules using external checkers. # # This is the main checker controlling the other ones and the reports # generation. It is itself both a raw checker and an astng checker in order # to: # * handle message activation / deactivation at the module level # * handle some basic but necessary stats'data (number of classes, methods...) # [MASTER] # Specify a configuration file. #rcfile= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Add to the black list. It should be a base name, not a # path. You may set this option multiple times. ignore= # Pickle collected data for later comparisons. persistent=no # Set the cache size for astng objects. cache-size=500 # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. load-plugins= extension-pkg-whitelist= greenlet [MESSAGES CONTROL] # Enable only checker(s) with the given id(s). This option conflicts with the # disable-checker option #enable-checker= # Enable all checker(s) except those with the given id(s). This option # conflicts with the enable-checker option #disable-checker= # Enable all messages in the listed categories. #enable-msg-cat= # Disable all messages in the listed categories. #disable-msg-cat= # Enable the message(s) with the given id(s). enable= useless-suppression # Disable the message(s) with the given id(s). disable= spelling, # Messages that are just silly: locally-disabled, exec-used, no-init, bad-whitespace, global-statement, broad-except, no-else-return, # Messages that may be silly: no-self-use, no-member, using-constant-test, too-many-nested-blocks, too-many-ancestors, # Formatting stuff superfluous-parens,bad-continuation, # I'm fine deciding my own import order, wrong-import-position, wrong-import-order, # Messages that are noisy for now, eventually maybe we'll turn them on: invalid-name, protected-access, duplicate-code, cyclic-import msg-template={path}:{line}: {msg} ({symbol}) [REPORTS] # set the output format. Available formats are text, parseable, colorized, msvs # (visual studio) and html output-format=text # Put messages in a separate file for each module / package specified on the # command line instead of printing them on stdout. Reports (if any) will be # written in a file name "pylint_global.[txt|html]". files-output=no # Tells wether to display a full report or only the messages reports=no # I don't need a score, thanks. score=no # Python expression which should return a note less than 10 (10 is the highest # note).You have access to the variables errors warning, statement which # respectively contain the number of errors / warnings messages and the total # number of statements analyzed. This is used by the global evaluation report # (R0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Enable the report(s) with the given id(s). #enable-report= # Disable the report(s) with the given id(s). #disable-report= # checks for : # * doc strings # * modules / classes / functions / methods / arguments / variables name # * number of arguments, local variables, branchs, returns and statements in # functions, methods # * required module attributes # * dangerous default values as arguments # * redefinition of function / method / class # * uses of the global statement # [BASIC] # Regular expression which should only match functions or classes name which do # not require a docstring # Special methods don't: __foo__ # Test methods don't: testXXXX # TestCase overrides don't: setUp, tearDown # Nested decorator implementations: _decorator, _wrapped # Dispatched methods don't: _xxx__Xxxx no-docstring-rgx=__.*__|test[A-Z_].*|setUp|tearDown|_decorator|_wrapped|_.*__.* # Regular expression which should only match correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Regular expression which should only match correct module level names const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # Regular expression which should only match correct class names class-rgx=[A-Z_][a-zA-Z0-9]+$ # Regular expression which should only match correct function names function-rgx=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match correct method names method-rgx=[a-z_][a-z0-9_]{2,30}$|setUp|tearDown|test_.* # Regular expression which should only match correct instance attribute names attr-rgx=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match correct argument names argument-rgx=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match correct variable names variable-rgx=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match correct list comprehension / # generator expression variable names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ # Good variable names which should always be accepted, separated by a comma good-names=i,j,k,ex,Run,_ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata # List of builtins function names that should not be used, separated by a comma bad-functions= # try to find bugs in the code using type inference # [TYPECHECK] # Tells wether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=yes # List of classes names for which member attributes should not be checked # (useful for classes with attributes dynamicaly set). ignored-classes=SQLObject # List of members which are usually get through zope's acquisition mecanism and # so shouldn't trigger E0201 when accessed (need zope=yes to be considered). acquired-members=REQUEST,acl_users,aq_parent # checks for # * unused variables / imports # * undefined variables # * redefinition of variable from builtins or from an outer scope # * use of variable before assigment # [VARIABLES] # Tells wether we should check for unused import in __init__ files. init-import=no # A regular expression matching names of unused arguments. ignored-argument-names=_|unused|.*_unused dummy-variables-rgx=_|unused|.*_unused # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. additional-builtins= # checks for : # * methods without self as first argument # * overridden methods signature # * access only to existent members via self # * attributes not defined in the __init__ method # * supported interfaces implementation # * unreachable code # [CLASSES] # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__,__new__,setUp,reset # checks for sign of poor/misdesign: # * number of methods, attributes, local variables... # * size, complexity of functions, methods # [DESIGN] # Maximum number of arguments for function / method max-args=15 # Maximum number of locals for function / method body max-locals=50 # Maximum number of return / yield for function / method body max-returns=20 # Maximum number of branch for function / method body max-branches=50 # Maximum number of statements in function / method body max-statements=150 # Maximum number of parents for a class (see R0901). max-parents=12 # Maximum number of attributes for a class (see R0902). max-attributes=40 # Minimum number of public methods for a class (see R0903). min-public-methods=0 # Maximum number of public methods for a class (see R0904). max-public-methods=500 # checks for # * external modules dependencies # * relative / wildcard imports # * cyclic imports # * uses of deprecated modules # [IMPORTS] # Deprecated modules which should not be used, separated by a comma deprecated-modules=regsub,string,TERMIOS,Bastion,rexec # Create a graph of every (i.e. internal and external) dependencies in the # given file (report R0402 must not be disabled) import-graph= # Create a graph of external dependencies in the given file (report R0402 must # not be disabled) ext-import-graph= # Create a graph of internal dependencies in the given file (report R0402 must # not be disabled) int-import-graph= # checks for : # * unauthorized constructions # * strict indentation # * line length # * use of <> instead of != # [FORMAT] # Maximum number of characters on a single line. max-line-length=100 # Maximum number of lines in a module max-module-lines=10000 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # checks for: # * warning notes in the code like FIXME, XXX # * PEP 263: source code with non ascii character but no encoding declaration # [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME,XXX,TODO # checks for similarities and duplicated code. This computation may be # memory / CPU intensive, so you should disable it if you experiments some # problems. # [SIMILARITIES] # Minimum lines number of a similarity. min-similarity-lines=4 # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes # # SPELLING # spelling-dict=en_US # pylint doesn't strip the words, so insert a dummy x at the beginning to make # the other words work properly. # https://bitbucket.org/logilab/pylint/issue/398/spelling-words-need-to-be-stripped-or-the spelling-private-dict-file=doc/dict.txt python-coverage-4.5+dfsg.1.orig/circle.yml0000644000076600000620000000047613146037571016460 0ustar staff# Circle CI configuration for coverage.py. # https://circleci.com/gh/nedbat/coveragepy machine: python: version: 2.7.6 post: - pyenv global pypy-2.4.0 2.6.8 2.7.9 3.3.3 3.4.2 dependencies: pre: - pip install -U pip override: - pip install -r requirements/tox.pip test: override: - tox