diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16cd01f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_STORE +*.swp diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..2d3579e --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,25 @@ +Change Log +---------- + +.. + All enhancements and patches to warnings_report will be documented + in this file. It adheres to the structure of http://keepachangelog.com/ , + but in reStructuredText instead of Markdown (for ease of incorporation into + Sphinx documentation and the PyPI description). + + This project adheres to Semantic Versioning (http://semver.org/). + +.. There should always be an "Unreleased" section for changes pending release. + +Unreleased +~~~~~~~~~~ + +* + +[0.1.0] - 2020-01-14 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Added +_____ + +* First release on PyPI. diff --git a/LICENSE b/LICENSE.txt similarity index 97% rename from LICENSE rename to LICENSE.txt index 0ad25db..7bc605e 100644 --- a/LICENSE +++ b/LICENSE.txt @@ -1,7 +1,8 @@ + GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -633,8 +634,8 @@ the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -643,7 +644,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -658,4 +659,16 @@ specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see -. +. + +EdX Inc. wishes to state, in clarification of the above license terms, that +any public, independently available web service offered over the network and +communicating with edX's copyrighted works by any form of inter-service +communication, including but not limited to Remote Procedure Call (RPC) +interfaces, is not a work based on our copyrighted work within the meaning +of the license. "Corresponding Source" of this work, or works based on this +work, as defined by the terms of this license do not include source code +files for programs used solely to provide those public, independently +available web services. + + diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..1ade348 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +include LICENSE +include README.rst + +recursive-exclude * __pycache__ +recursive-exclude * *.py[co] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..acbf880 --- /dev/null +++ b/Makefile @@ -0,0 +1,72 @@ +.PHONY: clean compile_translations coverage diff_cover docs dummy_translations \ + extract_translations fake_translations help pii_check pull_translations push_translations \ + quality requirements selfcheck test test-all upgrade validate + +.DEFAULT_GOAL := help + +# For opening files in a browser. Use like: $(BROWSER)relative/path/to/file.html +BROWSER := python -m webbrowser file://$(CURDIR)/ + +help: ## display this help message + @echo "Please use \`make ' where is one of" + @awk -F ':.*?## ' '/^[a-zA-Z]/ && NF==2 {printf "\033[36m %-25s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort + +clean: ## remove generated byte code, coverage reports, and build artifacts + find . -name '__pycache__' -exec rm -rf {} + + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} + + coverage erase + rm -fr build/ + rm -fr dist/ + rm -fr *.egg-info + +coverage: clean ## generate and view HTML coverage report + pytest --cov-report html + $(BROWSER)htmlcov/index.html + +docs: ## generate Sphinx HTML documentation, including API docs + tox -e docs + $(BROWSER)docs/_build/html/index.html + +# Define PIP_COMPILE_OPTS=-v to get more information during make upgrade. +PIP_COMPILE = pip-compile --rebuild --upgrade $(PIP_COMPILE_OPTS) + +upgrade: export CUSTOM_COMPILE_COMMAND=make upgrade +upgrade: ## update the requirements/*.txt files with the latest packages satisfying requirements/*.in + pip install -qr requirements/pip-tools.txt + # Make sure to compile files after any other files they include! + $(PIP_COMPILE) -o requirements/pip-tools.txt requirements/pip-tools.in + $(PIP_COMPILE) -o requirements/base.txt requirements/base.in + $(PIP_COMPILE) -o requirements/test.txt requirements/test.in + $(PIP_COMPILE) -o requirements/doc.txt requirements/doc.in + $(PIP_COMPILE) -o requirements/quality.txt requirements/quality.in + $(PIP_COMPILE) -o requirements/travis.txt requirements/travis.in + $(PIP_COMPILE) -o requirements/dev.txt requirements/dev.in + # Let tox control the Django version for tests + sed '/^[dD]jango==/d' requirements/test.txt > requirements/test.tmp + mv requirements/test.tmp requirements/test.txt + +quality: ## check coding style with pycodestyle and pylint + tox -e quality + +pii_check: ## check for PII annotations on all Django models + tox -e pii_check + +requirements: ## install development environment requirements + pip install -qr requirements/pip-tools.txt + pip-sync requirements/dev.txt requirements/private.* + +test: clean ## run tests in the current virtualenv + pytest + +diff_cover: test ## find diff lines that need test coverage + diff-cover coverage.xml + +test-all: quality pii_check ## run tests on every supported Python/Django combination + tox + +validate: quality pii_check test ## run tests and quality checks + +selfcheck: ## check that the Makefile is well-formed + @echo "The Makefile is well-formed." diff --git a/README.md b/README.md deleted file mode 100644 index f5e4de4..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# pytest-warnings-report -Create an aggregate report of the warnings seen during test runs. diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..abe8d0c --- /dev/null +++ b/README.rst @@ -0,0 +1,80 @@ +====================== +pytest-warnings-report +====================== + +.. image:: https://img.shields.io/pypi/v/pytest-warnings-report.svg + :target: https://pypi.org/project/pytest-warnings-report + :alt: PyPI version + +.. image:: https://img.shields.io/pypi/pyversions/pytest-warnings-report.svg + :target: https://pypi.org/project/pytest-warnings-report + :alt: Python versions + +.. image:: https://travis-ci.org/edx/pytest-warnings-report.svg?branch=master + :target: https://travis-ci.org/edx/pytest-warnings-report + :alt: See Build Status on Travis CI + +.. image:: https://ci.appveyor.com/api/projects/status/github/edx/pytest-warnings-report?branch=master + :target: https://ci.appveyor.com/project/edx/pytest-warnings-report/branch/master + :alt: See Build Status on AppVeyor + +A pytest plugin for generating warnings reports. + +---- + +This `pytest`_ plugin was generated with `Cookiecutter`_ along with `@hackebrot`_'s `cookiecutter-pytest-plugin`_ template. + + +Features +-------- + +* TODO + + +Requirements +------------ + +* TODO + + +Installation +------------ + +You can install "pytest-warnings-report" via `pip`_ from `PyPI`_:: + + $ pip install pytest-warnings-report + + +Usage +----- + +* TODO + +Contributing +------------ +Contributions are very welcome. Tests can be run with `tox`_, please ensure +the coverage at least stays the same before you submit a pull request. + +License +------- + +Distributed under the terms of the `GNU GPL v3.0`_ license, "pytest-warnings-report" is free and open source software + + +Issues +------ + +If you encounter any problems, please `file an issue`_ along with a detailed description. + +.. _`Cookiecutter`: https://github.com/audreyr/cookiecutter +.. _`@hackebrot`: https://github.com/hackebrot +.. _`MIT`: http://opensource.org/licenses/MIT +.. _`BSD-3`: http://opensource.org/licenses/BSD-3-Clause +.. _`GNU GPL v3.0`: http://www.gnu.org/licenses/gpl-3.0.txt +.. _`Apache Software License 2.0`: http://www.apache.org/licenses/LICENSE-2.0 +.. _`cookiecutter-pytest-plugin`: https://github.com/pytest-dev/cookiecutter-pytest-plugin +.. _`file an issue`: https://github.com/edx/pytest-warnings-report/issues +.. _`pytest`: https://github.com/pytest-dev/pytest +.. _`tox`: https://tox.readthedocs.io/en/latest/ +.. _`pip`: https://pypi.org/project/pip/ +.. _`PyPI`: https://pypi.org/project diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..c5d33c7 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,43 @@ +# What Python version is installed where: +# http://www.appveyor.com/docs/installed-software#python + +environment: + matrix: + - PYTHON: "C:\\Python27" + TOX_ENV: "py27" + + - PYTHON: "C:\\Python34" + TOX_ENV: "py34" + + - PYTHON: "C:\\Python35" + TOX_ENV: "py35" + + - PYTHON: "C:\\Python36" + TOX_ENV: "py36" + + - PYTHON: "C:\\Python37" + TOX_ENV: "py37" + +init: + - "%PYTHON%/python -V" + - "%PYTHON%/python -c \"import struct;print( 8 * struct.calcsize(\'P\'))\"" + +install: + - "%PYTHON%/Scripts/easy_install -U pip" + - "%PYTHON%/Scripts/pip install tox" + - "%PYTHON%/Scripts/pip install wheel" + +build: false # Not a C# project, build stuff at the test step instead. + +test_script: + - "%PYTHON%/Scripts/tox -e %TOX_ENV%" + +after_test: + - "%PYTHON%/python setup.py bdist_wheel" + - ps: "ls dist" + +artifacts: + - path: dist\* + +#on_success: +# - TODO: upload the content of dist/*.whl to a public wheelhouse diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..4da4768 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,12 @@ +coverage: + status: + project: + default: + enabled: yes + target: auto + patch: + default: + enabled: yes + target: 100% + +comment: false diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..2d87ff4 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,192 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pytest-cookiecutterplugin_name.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pytest-cookiecutterplugin_name.qhc" + +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/pytest-cookiecutterplugin_name" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pytest-cookiecutterplugin_name" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..d16aa78 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,286 @@ +# -*- coding: utf-8 -*- +# +# pytest-warnings-report documentation build configuration file, created by +# sphinx-quickstart on Thu Oct 1 00:43:18 2015. +# +# 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 +import os +import shlex + +# 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.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# 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.ifconfig', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'pytest-warnings-report' +copyright = u'2015, edX' +author = u'edX' + +# 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 = '0.0.1' +# The full version, including alpha/beta/rc tags. +release = '0.0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +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 patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_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 = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# 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 = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# 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'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# 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' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# 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_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# 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 = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# 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 = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'pytest-cookiecutterplugin_namedoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'pytest-cookiecutterplugin_name.tex', u'pytest-\\{\\{cookiecutter.plugin\\_name\\}\\} Documentation', + u'\\{\\{cookiecutter.full\\_name\\}\\}', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'pytest-cookiecutterplugin_name', u'pytest-warnings-report Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'pytest-cookiecutterplugin_name', u'pytest-warnings-report Documentation', + author, 'pytest-cookiecutterplugin_name', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..f84cede --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,22 @@ +.. pytest-warnings-report documentation master file, created by + sphinx-quickstart on Thu Oct 1 00:43:18 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to pytest-warnings-report's documentation! +=============================================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..28beda1 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pytest-cookiecutterplugin_name.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pytest-cookiecutterplugin_name.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/openedx.yaml b/openedx.yaml new file mode 100644 index 0000000..b20e13f --- /dev/null +++ b/openedx.yaml @@ -0,0 +1,17 @@ +# openedx.yaml + +--- +owner: edx/arch-bom +nick: warnings-report +tags: + - tools +oeps: + oep-2: True + oep-3: + state: False + reason: TODO - Implement for this application if appropriate + oep-5: + state: False + reason: TODO - Implement for this application if appropriate + oep-30: + state: True diff --git a/process_warnings.py b/process_warnings.py new file mode 100644 index 0000000..6c749e7 --- /dev/null +++ b/process_warnings.py @@ -0,0 +1,246 @@ +""" +Script to process pytest warnings output by pytest-json-report plugin and output it as a html +""" +from __future__ import absolute_import +from __future__ import print_function +import json +import os +import io +import re +import argparse +from collections import Counter +import pandas as pd + +from write_to_html import ( + HtmlOutlineWriter, +) # noqa pylint: disable=import-error,useless-suppression + +columns = [ + "message", + "category", + "filename", + "lineno", + "high_location", + "label", + "num", + "deprecated", +] +columns_index_dict = {key: index for index, key in enumerate(columns)} + + +def seperate_warnings_by_location(warnings_data): + """ + Warnings originate from multiple locations, this function takes in list of warning objects + and separates them based on their filename location + """ + + # first create regex for each n file location + warnings_locations = { + ".*/python\d\.\d/site-packages/.*\.py": "python", # noqa pylint: disable=W1401 + ".*/edx-platform/lms/.*\.py": "lms", # noqa pylint: disable=W1401 + ".*/edx-platform/openedx/.*\.py": "openedx", # noqa pylint: disable=W1401 + ".*/edx-platform/cms/.*\.py": "cms", # noqa pylint: disable=W1401 + ".*/edx-platform/common/.*\.py": "common", # noqa pylint: disable=W1401 + } + + # separate into locations flow: + # - iterate through each wanring_object, see if its filename matches any regex in warning locations. + # - If so, change high_location index on warnings_object to location name + for warnings_object in warnings_data: + warning_origin_located = False + for key in warnings_locations: + if ( + re.search(key, warnings_object[columns_index_dict["filename"]]) + is not None + ): + warnings_object[ + columns_index_dict["high_location"] + ] = warnings_locations[key] + warning_origin_located = True + break + if not warning_origin_located: + warnings_object[columns_index_dict["high_location"]] = "other" + return warnings_data + + +def convert_warning_dict_to_list(warning_dict): + """ + converts our data dict into our defined list based on columns defined at top of this file + """ + output = [] + for column in columns: + if column in warning_dict: + output.append(warning_dict[column]) + else: + output.append(None) + output[columns_index_dict["num"]] = 1 + return output + + +def read_warning_data(dir_path): + """ + During test runs in jenkins, multiple warning json files are output. This function finds all files + and aggregates the warnings in to one large list + """ + # pdb.set_trace() + dir_path = os.path.expanduser(dir_path) + # find all files that exist in given directory + files_in_dir = [ + f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f)) + ] + warnings_files = [] + + # TODO(jinder): currently this is hard-coded in, maybe create a constants file with info + # THINK(jinder): but creating file for one constant seems overkill + warnings_file_name_regex = ( + "pytest_warnings_?\d*\.json" # noqa pylint: disable=W1401 + ) + + # iterate through files_in_dir and see if they match our know file name pattern + for temp_file in files_in_dir: + if re.search(warnings_file_name_regex, temp_file) is not None: + warnings_files.append(temp_file) + + # go through each warning file and aggregate warnings into warnings_data + warnings_data = [] + for temp_file in warnings_files: + with io.open(os.path.expanduser(dir_path + "/" + temp_file), "r") as read_file: + json_input = json.load(read_file) + if "warnings" in json_input: + data = [ + convert_warning_dict_to_list(warning_dict) + for warning_dict in json_input["warnings"] + ] + warnings_data.extend(data) + else: + print(temp_file) + return warnings_data + + +def compress_similar_warnings(warnings_data): + """ + find all warnings that are exactly the same, count them, and return set with count added to each warning + """ + tupled_data = [tuple(data) for data in warnings_data] + test_counter = Counter(tupled_data) + output = [list(value) for value in test_counter.keys()] + for data_object in output: + data_object[columns_index_dict["num"]] = test_counter[tuple(data_object)] + return output + + +def process_warnings_json(dir_path): + """ + Master function to process through all warnings and output a dict + + dict structure: + { + location: [{warning text: {file_name: warning object}}] + } + + flow: + - Aggregate data from all warning files + - Separate warnings by deprecated vs non deprecated(has word deprecate in it) + - Further categorize warnings + - Return output + Possible Error/enhancement: there might be better ways to separate deprecates vs + non-deprecated warnings + """ + warnings_data = read_warning_data(dir_path) + for warnings_object in warnings_data: + warnings_object[columns_index_dict["deprecated"]] = bool( + "deprecated" in warnings_object[columns_index_dict["message"]] + ) + warnings_data = seperate_warnings_by_location(warnings_data) + compressed_warnings_data = compress_similar_warnings(warnings_data) + return compressed_warnings_data + + +def group_and_sort_by_sumof(dataframe, group, sort_by): + groups_by = dataframe.groupby(group) + temp_list_to_sort = [(key, value, value[sort_by].sum()) for key, value in groups_by] + # sort by count + return sorted(temp_list_to_sort, key=lambda x: -x[2]) + + +def write_html_report(warnings_dataframe, html_path): + """ + converts from panda dataframe to our html + """ + html_path = os.path.expanduser(html_path) + if "/" in html_path: + location_of_last_dir = html_path.rfind("/") + dir_path = html_path[:location_of_last_dir] + os.makedirs(dir_path, exist_ok=True) + with io.open(html_path, "w") as fout: + html_writer = HtmlOutlineWriter(fout) + category_sorted_by_count = group_and_sort_by_sumof( + warnings_dataframe, "category", "num" + ) + for category, group_in_category, category_count in category_sorted_by_count: + # xss-lint: disable=python-wrap-html + html = u'{category}, count: {count} '.format( + category=category, count=category_count + ) + html_writer.start_section(html, klass=u"category") + locations_sorted_by_count = group_and_sort_by_sumof( + group_in_category, "high_location", "num" + ) + + for ( + location, + group_in_location, + location_count, + ) in locations_sorted_by_count: + # xss-lint: disable=python-wrap-html + html = u'{location}, count: {count} '.format( + location=location, count=location_count + ) + html_writer.start_section(html, klass=u"location") + message_group_sorted_by_count = group_and_sort_by_sumof( + group_in_location, "message", "num" + ) + for ( + message, + message_group, + message_count, + ) in message_group_sorted_by_count: + # xss-lint: disable=python-wrap-html + html = u'{warning_text}, count: {count} '.format( + warning_text=message, count=message_count + ) + html_writer.start_section(html, klass=u"warning_text") + # warnings_object[location][warning_text] is a list + for _, warning in message_group.iterrows(): + # xss-lint: disable=python-wrap-html + html = u'{warning_file_path} '.format( + warning_file_path=warning["filename"] + ) + html_writer.start_section(html, klass=u"warning") + # xss-lint: disable=python-wrap-html + html = u'

lineno: {lineno}

'.format( + lineno=warning["lineno"] + ) + html_writer.write(html) + # xss-lint: disable=python-wrap-html + html = u'

num_occur: {num}

'.format( + num=warning["num"] + ) + html_writer.write(html) + + html_writer.end_section() + html_writer.end_section() + html_writer.end_section() + html_writer.end_section() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Process and categorize pytest warnings and output html report." + ) + parser.add_argument("--dir-path", default="test_root/log") + parser.add_argument("--html-path", default="test_html.html") + args = parser.parse_args() + data_output = process_warnings_json(args.dir_path) + data_dataframe = pd.DataFrame(data=data_output, columns=columns) + write_html_report(data_dataframe, args.html_path) diff --git a/pylintrc b/pylintrc new file mode 100644 index 0000000..b434705 --- /dev/null +++ b/pylintrc @@ -0,0 +1,456 @@ +# *************************** +# ** DO NOT EDIT THIS FILE ** +# *************************** +# +# This file was generated by edx-lint: http://github.com/edx/edx-lint +# +# If you want to change this file, you have two choices, depending on whether +# you want to make a local change that applies only to this repo, or whether +# you want to make a central change that applies to all repos using edx-lint. +# +# LOCAL CHANGE: +# +# 1. Edit the local pylintrc_tweaks file to add changes just to this +# repo's file. +# +# 2. Run: +# +# $ edx_lint write pylintrc +# +# 3. This will modify the local file. Submit a pull request to get it +# checked in so that others will benefit. +# +# +# CENTRAL CHANGE: +# +# 1. Edit the pylintrc file in the edx-lint repo at +# https://github.com/edx/edx-lint/blob/master/edx_lint/files/pylintrc +# +# 2. install the updated version of edx-lint (in edx-lint): +# +# $ pip install . +# +# 3. Run (in edx-lint): +# +# # uses pylintrc_tweaks from edx-lint for linting in edx-lint +# # NOTE: Use Python 3.x, which no longer includes comments in the output file +# $ edx_lint write pylintrc +# +# 4. Make a new version of edx_lint, submit and review a pull request with the +# pylintrc update, and after merging, update the edx-lint version by +# creating a new tag in the repo (uses pbr). +# +# 5. In your local repo, install the newer version of edx-lint. +# +# 6. Run: +# +# # uses local pylintrc_tweaks +# $ edx_lint write pylintrc +# +# 7. This will modify the local file. Submit a pull request to get it +# checked in so that others will benefit. +# +# +# +# +# +# STAY AWAY FROM THIS FILE! +# +# +# +# +# +# SERIOUSLY. +# +# ------------------------------ +[MASTER] +ignore = migrations +persistent = yes +load-plugins = edx_lint.pylint,pylint_django,pylint_celery + +[MESSAGES CONTROL] +enable = + blacklisted-name, + line-too-long, + + syntax-error, + init-is-generator, + return-in-init, + function-redefined, + not-in-loop, + return-outside-function, + yield-outside-function, + return-arg-in-generator, + nonexistent-operator, + duplicate-argument-name, + abstract-class-instantiated, + bad-reversed-sequence, + continue-in-finally, + method-hidden, + access-member-before-definition, + no-method-argument, + no-self-argument, + invalid-slots-object, + assigning-non-slot, + invalid-slots, + inherit-non-class, + inconsistent-mro, + duplicate-bases, + non-iterator-returned, + unexpected-special-method-signature, + invalid-length-returned, + import-error, + used-before-assignment, + undefined-variable, + undefined-all-variable, + invalid-all-object, + no-name-in-module, + unbalance-tuple-unpacking, + unpacking-non-sequence, + bad-except-order, + raising-bad-type, + misplaced-bare-raise, + raising-non-exception, + nonimplemented-raised, + catching-non-exception, + slots-on-old-class, + super-on-old-class, + bad-super-call, + missing-super-argument, + no-member, + not-callable, + assignment-from-no-return, + no-value-for-parameter, + too-many-function-args, + unexpected-keyword-arg, + redundant-keyword-arg, + invalid-sequence-index, + invalid-slice-index, + assignment-from-none, + not-context-manager, + invalid-unary-operand-type, + unsupported-binary-operation, + repeated-keyword, + not-an-iterable, + not-a-mapping, + unsupported-membership-test, + unsubscriptable-object, + logging-unsupported-format, + logging-too-many-args, + logging-too-few-args, + bad-format-character, + truncated-format-string, + mixed-fomat-string, + format-needs-mapping, + missing-format-string-key, + too-many-format-args, + too-few-format-args, + bad-str-strip-call, + model-unicode-not-callable, + super-method-not-called, + non-parent-method-called, + test-inherits-tests, + translation-of-non-string, + redefined-variable-type, + cyclical-import, + unreachable, + dangerous-default-value, + pointless-statement, + pointless-string-statement, + expression-not-assigned, + duplicate-key, + confusing-with-statement, + using-constant-test, + lost-exception, + assert-on-tuple, + attribute-defined-outside-init, + bad-staticmethod-argument, + arguments-differ, + signature-differs, + abstract-method, + super-init-not-called, + relative-import, + import-self, + misplaced-future, + invalid-encoded-data, + global-variable-undefined, + redefined-outer-name, + redefined-builtin, + redefined-in-handler, + undefined-loop-variable, + cell-var-from-loop, + duplicate-except, + nonstandard-exception, + binary-op-exception, + property-on-old-class, + bad-format-string-key, + unused-format-string-key, + bad-format-string, + missing-format-argument-key, + unused-format-string-argument, + format-combined-specification, + missing-format-attribute, + invalid-format-index, + anomalous-backslash-in-string, + anomalous-unicode-escape-in-string, + bad-open-mode, + boolean-datetime, + + fatal, + astroid-error, + parse-error, + method-check-failed, + django-not-available, + raw-checker-failed, + django-not-available-placeholder, + + empty-docstring, + invalid-characters-in-docstring, + missing-docstring, + wrong-spelling-in-comment, + wrong-spelling-in-docstring, + + unused-import, + unused-variable, + unused-argument, + + exec-used, + eval-used, + + bad-classmethod-argument, + bad-mcs-classmethod-argument, + bad-mcs-method-argument, + bad-whitespace, + consider-iterating-dictionary, + consider-using-enumerate, + literal-used-as-attribute, + multiple-imports, + multiple-statements, + old-style-class, + simplifiable-range, + singleton-comparison, + superfluous-parens, + unidiomatic-typecheck, + unneeded-not, + wrong-assert-type, + simplifiable-if-statement, + no-classmethod-decorator, + no-staticmethod-decorator, + unnecessary-pass, + unnecessary-lambda, + useless-else-on-loop, + unnecessary-semicolon, + reimported, + global-variable-not-assigned, + global-at-module-level, + bare-except, + broad-except, + logging-not-lazy, + redundant-unittest-assert, + model-missing-unicode, + model-has-unicode, + model-no-explicit-unicode, + protected-access, + + deprecated-module, + deprecated-method, + + too-many-nested-blocks, + too-many-statements, + too-many-boolean-expressions, + + wrong-import-order, + wrong-import-position, + wildcard-import, + + missing-final-newline, + mixed-line-endings, + trailing-newlines, + trailing-whitespace, + unexpected-line-ending-format, + mixed-indentation, + + bad-option-value, + unrecognized-inline-option, + useless-suppression, + bad-inline-option, + deprecated-pragma, +disable = + bad-continuation, + invalid-name, + misplaced-comparison-constant, + file-ignored, + bad-indentation, + lowercase-l-suffix, + unused-wildcard-import, + global-statement, + no-else-return, + + apply-builtin, + backtick, + basestring-builtin, + buffer-builtin, + cmp-builtin, + cmp-method, + coerce-builtin, + coerce-method, + delslice-method, + dict-iter-method, + dict-view-method, + duplicate-code, + execfile-builtin, + feature-toggle-needs-doc, + file-builtin, + filter-builtin-not-iterating, + fixme, + getslice-method, + hex-method, + illegal-waffle-usage, + import-star-module-level, + indexing-exception, + input-builtin, + intern-builtin, + locally-disabled, + locally-enabled, + logging-format-interpolation, + long-builtin, + long-suffix, + map-builtin-not-iterating, + metaclass-assignment, + next-method-called, + no-absolute-import, + no-init, + no-self-use, + nonzero-method, + oct-method, + old-division, + old-ne-operator, + old-octal-literal, + old-raise-syntax, + parameter-unpacking, + print-statement, + raising-string, + range-builtin-not-iterating, + raw_input-builtin, + reduce-builtin, + reload-builtin, + round-builtin, + setslice-method, + standarderror-builtin, + suppressed-message, + too-few-public-methods, + too-many-ancestors, + too-many-arguments, + too-many-branches, + too-many-instance-attributes, + too-many-lines, + too-many-locals, + too-many-public-methods, + too-many-return-statements, + ungrouped-imports, + unichr-builtin, + unicode-builtin, + unpacking-in-except, + using-cmp-argument, + xrange-builtin, + zip-builtin-not-iterating, + +[REPORTS] +output-format = text +files-output = no +reports = no +evaluation = 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +[BASIC] +bad-functions = map,filter,apply,input +module-rgx = (([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ +const-rgx = (([A-Z_][A-Z0-9_]*)|(__.*__)|log|urlpatterns)$ +class-rgx = [A-Z_][a-zA-Z0-9]+$ +function-rgx = ([a-z_][a-z0-9_]{2,40}|test_[a-z0-9_]+)$ +method-rgx = ([a-z_][a-z0-9_]{2,40}|setUp|set[Uu]pClass|tearDown|tear[Dd]ownClass|assert[A-Z]\w*|maxDiff|test_[a-z0-9_]+)$ +attr-rgx = [a-z_][a-z0-9_]{2,30}$ +argument-rgx = [a-z_][a-z0-9_]{2,30}$ +variable-rgx = [a-z_][a-z0-9_]{2,30}$ +class-attribute-rgx = ([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ +inlinevar-rgx = [A-Za-z_][A-Za-z0-9_]*$ +good-names = f,i,j,k,db,ex,Run,_,__ +bad-names = foo,bar,baz,toto,tutu,tata +no-docstring-rgx = __.*__$|test_.+|setUp$|setUpClass$|tearDown$|tearDownClass$|Meta$ +docstring-min-length = 5 + +[FORMAT] +max-line-length = 120 +ignore-long-lines = ^\s*(# )?((?)|(\.\. \w+: .*))$ +single-line-if-stmt = no +no-space-check = trailing-comma,dict-separator +max-module-lines = 1000 +indent-string = ' ' + +[MISCELLANEOUS] +notes = FIXME,XXX,TODO + +[SIMILARITIES] +min-similarity-lines = 4 +ignore-comments = yes +ignore-docstrings = yes +ignore-imports = no + +[TYPECHECK] +ignore-mixin-members = yes +ignored-classes = SQLObject +unsafe-load-any-extension = yes +generated-members = + REQUEST, + acl_users, + aq_parent, + objects, + DoesNotExist, + can_read, + can_write, + get_url, + size, + content, + status_code, + create, + build, + fields, + tag, + org, + course, + category, + name, + revision, + _meta, + +[VARIABLES] +init-import = no +dummy-variables-rgx = _|dummy|unused|.*_unused +additional-builtins = + +[CLASSES] +defining-attr-methods = __init__,__new__,setUp +valid-classmethod-first-arg = cls +valid-metaclass-classmethod-first-arg = mcs + +[DESIGN] +max-args = 5 +ignored-argument-names = _.* +max-locals = 15 +max-returns = 6 +max-branches = 12 +max-statements = 50 +max-parents = 7 +max-attributes = 7 +min-public-methods = 2 +max-public-methods = 20 + +[IMPORTS] +deprecated-modules = regsub,TERMIOS,Bastion,rexec +import-graph = +ext-import-graph = +int-import-graph = + +[EXCEPTIONS] +overgeneral-exceptions = Exception + +# 37b04aff3c193dcfd9d03f79cd129e2ac70c1100 diff --git a/pylintrc_tweaks b/pylintrc_tweaks new file mode 100644 index 0000000..4764e22 --- /dev/null +++ b/pylintrc_tweaks @@ -0,0 +1,4 @@ +# pylintrc tweaks for use with edx_lint. +[MASTER] +ignore = migrations +load-plugins = edx_lint.pylint,pylint_django,pylint_celery diff --git a/pytest_warnings_report.py b/pytest_warnings_report.py new file mode 100644 index 0000000..9f992f8 --- /dev/null +++ b/pytest_warnings_report.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- + +""" +Module to put all pytest hooks that modify pytest behaviour +""" +import os +import io +import json + + +def pytest_json_modifyreport(json_report): + """ + - The function is called by pytest-json-report plugin to only output warnings in json format. + - Everything else is removed due to it already being saved by junitxml + - --json-omit flag in does not allow us to remove everything but the warnings + - (the environment metadata is one example of unremoveable data) + - The json warning outputs are meant to be read by jenkins + """ + warnings_flag = "warnings" + if warnings_flag in json_report: + warnings = json_report[warnings_flag] + json_report.clear() + json_report[warnings_flag] = warnings + else: + json_report = {} + return json_report + + +def create_file_name(dir_path, file_name_postfix, num=0): + """ + Used to create file name with this given + structure: TEST_SUITE + "_" + file_name_postfix + "_ " + num.json + The env variable TEST_SUITE is set in jenkinsfile + + This was necessary cause Pytest is run multiple times and we need to make sure old pytest + warning json files are not being overwritten. + """ + name = dir_path + "/" + if "TEST_SUITE" in os.environ: + name += os.environ["TEST_SUITE"] + "_" + name += file_name_postfix + if num != 0: + name += "_" + str(num) + return name + ".json" + + +def pytest_sessionfinish(session): + """ + Since multiple pytests are running, + this makes sure warnings from different run are not overwritten + """ + dir_path = "test_root/log" + file_name_postfix = "pytest_warnings" + num = 0 + # to make sure this doesn't loop forever, putting a maximum + while ( + os.path.isfile(create_file_name(dir_path, file_name_postfix, num)) and num < 100 + ): + num += 1 + + report = session.config._json_report.report # noqa pylint: disable=protected-access + + with io.open(create_file_name(dir_path, file_name_postfix, num), "w") as outfile: + json.dump(report, outfile) + + +class DeferPlugin(object): + """Simple plugin to defer pytest-xdist hook functions.""" + + def pytest_json_modifyreport(self, json_report): + """standard xdist hook function. + """ + return pytest_json_modifyreport(json_report) + + def pytest_sessionfinish(self, session): + return pytest_sessionfinish(session) + + +def pytest_configure(config): + if config.pluginmanager.hasplugin("json-report"): + config.pluginmanager.register(DeferPlugin()) + diff --git a/requirements/base.in b/requirements/base.in new file mode 100644 index 0000000..3b36824 --- /dev/null +++ b/requirements/base.in @@ -0,0 +1,6 @@ +# Core requirements for using this application +-c constraints.txt + +pandas +pytest>=3.5.0 +pytest-json-report diff --git a/requirements/base.txt b/requirements/base.txt new file mode 100644 index 0000000..48122b9 --- /dev/null +++ b/requirements/base.txt @@ -0,0 +1,29 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +atomicwrites==1.3.0 # via pytest +attrs==19.3.0 # via pytest +configparser==4.0.2 # via importlib-metadata +contextlib2==0.6.0.post1 # via importlib-metadata +funcsigs==1.0.2 # via pytest +importlib-metadata==1.4.0 # via pluggy, pytest +more-itertools==5.0.0 # via pytest, zipp +numpy==1.16.6 # via pandas +packaging==20.0 # via pytest +pandas==0.24.2 +pathlib2==2.3.5 # via importlib-metadata, pytest +pluggy==0.13.1 # via pytest +py==1.8.1 # via pytest +pyparsing==2.4.6 # via packaging +pytest-json-report==1.2.1 +pytest-metadata==1.8.0 # via pytest-json-report +pytest==4.6.9 +python-dateutil==2.8.1 # via pandas +pytz==2019.3 # via pandas +scandir==1.10.0 # via pathlib2 +six==1.13.0 # via more-itertools, packaging, pathlib2, pytest, python-dateutil +wcwidth==0.1.8 # via pytest +zipp==1.0.0 # via importlib-metadata diff --git a/requirements/constraints.txt b/requirements/constraints.txt new file mode 100644 index 0000000..bd95a8f --- /dev/null +++ b/requirements/constraints.txt @@ -0,0 +1,15 @@ +# Version constraints for pip-installation. +# +# This file doesn't install any packages. It specifies version constraints +# that will be applied if a package is needed. +# +# When pinning something here, please provide an explanation of why. Ideally, +# link to other information that will help people in the future to remove the +# pin when possible. Writing an issue against the offending project and +# linking to it here is good. + +# code-annotations currently restricts to Django<2.3 +Django<2.3 + +# Version 4.0.0 dropped support for Django < 2.0.1 +django-model-utils<4.0.0 diff --git a/requirements/dev.in b/requirements/dev.in new file mode 100644 index 0000000..fa27299 --- /dev/null +++ b/requirements/dev.in @@ -0,0 +1,9 @@ +# Additional requirements for development of this application +-c constraints.txt + +-r pip-tools.txt # pip-tools and its dependencies, for managing requirements files +-r quality.txt # Core and quality check dependencies +-r travis.txt # tox and related dependencies + +diff-cover # Changeset diff test coverage +edx-i18n-tools # For i18n_tool dummy diff --git a/requirements/dev.txt b/requirements/dev.txt new file mode 100644 index 0000000..30e28f8 --- /dev/null +++ b/requirements/dev.txt @@ -0,0 +1,79 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +astroid==1.6.6 +atomicwrites==1.3.0 +attrs==19.3.0 +backports.functools-lru-cache==1.6.1 +certifi==2019.11.28 +chardet==3.0.4 +click-log==0.3.2 +click==7.0 +code-annotations==0.3.3 +codecov==2.0.15 +configparser==4.0.2 +contextlib2==0.6.0.post1 +coverage==5.0.3 +diff-cover==2.5.2 +django==1.11.27 +edx-i18n-tools==0.5.0 +edx-lint==1.4.1 +enum34==1.1.6 +filelock==3.0.12 +funcsigs==1.0.2 +futures==3.3.0 +idna==2.8 +importlib-metadata==1.4.0 +inflect==3.0.2 # via jinja2-pluralize +isort==4.3.21 +jinja2-pluralize==0.3.0 # via diff-cover +jinja2==2.10.3 +lazy-object-proxy==1.4.3 +markupsafe==1.1.1 +mccabe==0.6.1 +more-itertools==5.0.0 +numpy==1.16.6 +packaging==20.0 +pandas==0.24.2 +path.py==11.5.2 # via edx-i18n-tools +pathlib2==2.3.5 +pbr==5.4.4 +pip-tools==4.3.0 +pluggy==0.13.1 +polib==1.1.0 # via edx-i18n-tools +py==1.8.1 +pycodestyle==2.5.0 +pydocstyle==3.0.0 +pygments==2.5.2 # via diff-cover +pylint-celery==0.3 +pylint-django==0.11.1 +pylint-plugin-utils==0.6 +pylint==1.9.5 +pyparsing==2.4.6 +pytest-cov==2.8.1 +pytest-django==3.8.0 +pytest-json-report==1.2.1 +pytest-metadata==1.8.0 +pytest==4.6.9 +python-dateutil==2.8.1 +python-slugify==4.0.0 +pytz==2019.3 +pyyaml==5.3 +requests==2.22.0 +scandir==1.10.0 +singledispatch==3.4.0.3 +six==1.13.0 +snowballstemmer==2.0.0 +stevedore==1.31.0 +text-unidecode==1.3 +toml==0.10.0 +tox-battery==0.5.1 +tox==3.14.3 +urllib3==1.25.7 +virtualenv==16.7.9 +wcwidth==0.1.8 +wrapt==1.11.2 +zipp==1.0.0 diff --git a/requirements/doc.in b/requirements/doc.in new file mode 100644 index 0000000..690e8e1 --- /dev/null +++ b/requirements/doc.in @@ -0,0 +1,9 @@ +# Requirements for documentation validation +-c constraints.txt + +-r test.txt # Core and testing dependencies for this package + +doc8 # reStructuredText style checker +edx_sphinx_theme # edX theme for Sphinx output +readme_renderer # Validates README.rst for usage on PyPI +Sphinx # Documentation builder diff --git a/requirements/doc.txt b/requirements/doc.txt new file mode 100644 index 0000000..65c6300 --- /dev/null +++ b/requirements/doc.txt @@ -0,0 +1,65 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +alabaster==0.7.12 # via sphinx +atomicwrites==1.3.0 +attrs==19.3.0 +babel==2.8.0 # via sphinx +bleach==3.1.0 # via readme-renderer +certifi==2019.11.28 # via requests +chardet==3.0.4 # via doc8, requests +click==7.0 +code-annotations==0.3.3 +configparser==4.0.2 +contextlib2==0.6.0.post1 +coverage==5.0.3 +django==1.11.27 +doc8==0.8.0 +docutils==0.16 # via doc8, readme-renderer, restructuredtext-lint, sphinx +edx-sphinx-theme==1.5.0 +funcsigs==1.0.2 +idna==2.8 # via requests +imagesize==1.2.0 # via sphinx +importlib-metadata==1.4.0 +jinja2==2.10.3 +markupsafe==1.1.1 +more-itertools==5.0.0 +numpy==1.16.6 +packaging==20.0 +pandas==0.24.2 +pathlib2==2.3.5 +pbr==5.4.4 +pluggy==0.13.1 +py==1.8.1 +pygments==2.5.2 # via readme-renderer, sphinx +pyparsing==2.4.6 +pytest-cov==2.8.1 +pytest-django==3.8.0 +pytest-json-report==1.2.1 +pytest-metadata==1.8.0 +pytest==4.6.9 +python-dateutil==2.8.1 +python-slugify==4.0.0 +pytz==2019.3 +pyyaml==5.3 +readme-renderer==24.0 +requests==2.22.0 # via sphinx +restructuredtext-lint==1.3.0 # via doc8 +scandir==1.10.0 +six==1.13.0 +snowballstemmer==2.0.0 # via sphinx +sphinx==1.8.5 +sphinxcontrib-websupport==1.1.2 # via sphinx +stevedore==1.31.0 +text-unidecode==1.3 +typing==3.7.4.1 # via sphinx +urllib3==1.25.7 # via requests +wcwidth==0.1.8 +webencodings==0.5.1 # via bleach +zipp==1.0.0 + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/requirements/pip-tools.in b/requirements/pip-tools.in new file mode 100644 index 0000000..3f1b64a --- /dev/null +++ b/requirements/pip-tools.in @@ -0,0 +1,4 @@ +# Just the dependencies to run pip-tools, mainly for the "upgrade" make target +-c constraints.txt + +pip-tools # Contains pip-compile, used to generate pip requirements files diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt new file mode 100644 index 0000000..12b8b2d --- /dev/null +++ b/requirements/pip-tools.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +click==7.0 # via pip-tools +pip-tools==4.3.0 +six==1.13.0 # via pip-tools diff --git a/requirements/private.readme b/requirements/private.readme new file mode 100644 index 0000000..5600a10 --- /dev/null +++ b/requirements/private.readme @@ -0,0 +1,15 @@ +# If there are any Python packages you want to keep in your virtualenv beyond +# those listed in the official requirements files, create a "private.in" file +# and list them there. Generate the corresponding "private.txt" file pinning +# all of their indirect dependencies to specific versions as follows: + +# pip-compile private.in + +# This allows you to use "pip-sync" without removing these packages: + +# pip-sync requirements/*.txt + +# "private.in" and "private.txt" aren't checked into git to avoid merge +# conflicts, and the presence of this file allows "private.*" to be +# included in scripted pip-sync usage without requiring that those files be +# created first. diff --git a/requirements/quality.in b/requirements/quality.in new file mode 100644 index 0000000..0bd84a6 --- /dev/null +++ b/requirements/quality.in @@ -0,0 +1,9 @@ +# Requirements for code quality checks +-c constraints.txt + +-r test.txt # Core and testing dependencies for this package + +edx-lint # edX pylint rules and plugins +isort # to standardize order of imports +pycodestyle # PEP 8 compliance validation +pydocstyle # PEP 257 compliance validation diff --git a/requirements/quality.txt b/requirements/quality.txt new file mode 100644 index 0000000..9b6b3b2 --- /dev/null +++ b/requirements/quality.txt @@ -0,0 +1,60 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +astroid==1.6.6 # via pylint, pylint-celery +atomicwrites==1.3.0 +attrs==19.3.0 +backports.functools-lru-cache==1.6.1 # via astroid, isort, pylint +click-log==0.3.2 # via edx-lint +click==7.0 +code-annotations==0.3.3 +configparser==4.0.2 +contextlib2==0.6.0.post1 +coverage==5.0.3 +django==1.11.27 +edx-lint==1.4.1 +enum34==1.1.6 # via astroid +funcsigs==1.0.2 +futures==3.3.0 # via isort +importlib-metadata==1.4.0 +isort==4.3.21 +jinja2==2.10.3 +lazy-object-proxy==1.4.3 # via astroid +markupsafe==1.1.1 +mccabe==0.6.1 # via pylint +more-itertools==5.0.0 +numpy==1.16.6 +packaging==20.0 +pandas==0.24.2 +pathlib2==2.3.5 +pbr==5.4.4 +pluggy==0.13.1 +py==1.8.1 +pycodestyle==2.5.0 +pydocstyle==3.0.0 +pylint-celery==0.3 # via edx-lint +pylint-django==0.11.1 # via edx-lint +pylint-plugin-utils==0.6 # via pylint-celery, pylint-django +pylint==1.9.5 # via edx-lint, pylint-celery, pylint-django, pylint-plugin-utils +pyparsing==2.4.6 +pytest-cov==2.8.1 +pytest-django==3.8.0 +pytest-json-report==1.2.1 +pytest-metadata==1.8.0 +pytest==4.6.9 +python-dateutil==2.8.1 +python-slugify==4.0.0 +pytz==2019.3 +pyyaml==5.3 +scandir==1.10.0 +singledispatch==3.4.0.3 # via astroid, pylint +six==1.13.0 +snowballstemmer==2.0.0 # via pydocstyle +stevedore==1.31.0 +text-unidecode==1.3 +wcwidth==0.1.8 +wrapt==1.11.2 # via astroid +zipp==1.0.0 diff --git a/requirements/test.in b/requirements/test.in new file mode 100644 index 0000000..6797160 --- /dev/null +++ b/requirements/test.in @@ -0,0 +1,8 @@ +# Requirements for test runs. +-c constraints.txt + +-r base.txt # Core dependencies for this package + +pytest-cov # pytest extension for code coverage statistics +pytest-django # pytest extension for better Django support +code-annotations # provides commands used by the pii_check make target. diff --git a/requirements/test.txt b/requirements/test.txt new file mode 100644 index 0000000..6fe9840 --- /dev/null +++ b/requirements/test.txt @@ -0,0 +1,41 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +atomicwrites==1.3.0 +attrs==19.3.0 +click==7.0 # via code-annotations +code-annotations==0.3.3 +configparser==4.0.2 +contextlib2==0.6.0.post1 +coverage==5.0.3 # via pytest-cov +funcsigs==1.0.2 +importlib-metadata==1.4.0 +jinja2==2.10.3 # via code-annotations +markupsafe==1.1.1 # via jinja2 +more-itertools==5.0.0 +numpy==1.16.6 +packaging==20.0 +pandas==0.24.2 +pathlib2==2.3.5 +pbr==5.4.4 # via stevedore +pluggy==0.13.1 +py==1.8.1 +pyparsing==2.4.6 +pytest-cov==2.8.1 +pytest-django==3.8.0 +pytest-json-report==1.2.1 +pytest-metadata==1.8.0 +pytest==4.6.9 +python-dateutil==2.8.1 +python-slugify==4.0.0 # via code-annotations +pytz==2019.3 +pyyaml==5.3 # via code-annotations +scandir==1.10.0 +six==1.13.0 +stevedore==1.31.0 # via code-annotations +text-unidecode==1.3 # via python-slugify +wcwidth==0.1.8 +zipp==1.0.0 diff --git a/requirements/travis.in b/requirements/travis.in new file mode 100644 index 0000000..58bb23f --- /dev/null +++ b/requirements/travis.in @@ -0,0 +1,6 @@ +# Requirements for running tests in Travis +-c constraints.txt + +codecov # Code coverage reporting +tox # Virtualenv management for tests +tox-battery # Makes tox aware of requirements file changes diff --git a/requirements/travis.txt b/requirements/travis.txt new file mode 100644 index 0000000..df33c8f --- /dev/null +++ b/requirements/travis.txt @@ -0,0 +1,30 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +certifi==2019.11.28 # via requests +chardet==3.0.4 # via requests +codecov==2.0.15 +configparser==4.0.2 # via importlib-metadata +contextlib2==0.6.0.post1 # via importlib-metadata +coverage==5.0.3 # via codecov +filelock==3.0.12 # via tox +idna==2.8 # via requests +importlib-metadata==1.4.0 # via pluggy, tox +more-itertools==5.0.0 # via zipp +packaging==20.0 # via tox +pathlib2==2.3.5 # via importlib-metadata +pluggy==0.13.1 # via tox +py==1.8.1 # via tox +pyparsing==2.4.6 # via packaging +requests==2.22.0 # via codecov +scandir==1.10.0 # via pathlib2 +six==1.13.0 # via more-itertools, packaging, pathlib2, tox +toml==0.10.0 # via tox +tox-battery==0.5.1 +tox==3.14.3 +urllib3==1.25.7 # via requests +virtualenv==16.7.9 # via tox +zipp==1.0.0 # via importlib-metadata diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..a1f287b --- /dev/null +++ b/setup.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import re +import sys +from setuptools import setup + + +def get_version(*file_paths): + """ + Extract the version string from the file at the given relative path fragments. + """ + filename = os.path.join(os.path.dirname(__file__), *file_paths) + version_file = open(filename).read() + version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", + version_file, re.M) + if version_match: + return version_match.group(1) + raise RuntimeError('Unable to find version string.') + + +def load_requirements(*requirements_paths): + """ + Load all requirements from the specified requirements files. + + Returns: + list: Requirements file relative path strings + """ + requirements = set() + for path in requirements_paths: + requirements.update( + line.split('#')[0].strip() for line in open(path).readlines() + if is_requirement(line.strip()) + ) + return list(requirements) + + +def is_requirement(line): + """ + Return True if the requirement line is a package requirement. + + Returns: + bool: True if the line is not blank, a comment, a URL, or an included file + """ + return line and not line.startswith(('-r', '#', '-e', 'git+', '-c')) + + +VERSION = get_version('warnings_report', '__init__.py') + +if sys.argv[-1] == 'tag': + print("Tagging the version on github:") + os.system(u"git tag -a %s -m 'version %s'" % (VERSION, VERSION)) + os.system("git push --tags") + sys.exit() + +README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read() +CHANGELOG = open(os.path.join(os.path.dirname(__file__), 'CHANGELOG.rst')).read() + + +setup( + name='pytest-warnings-report', + version='0.0.1', + author='edX', + author_email='oscm@edx.org', + maintainer='edX', + maintainer_email='oscm@edx.org', + license='AGPL', + url='https://github.com/edx/pytest-warnings-report', + description='A pytest plugin for generating warnings reports.', + long_description=read('README.rst'), + py_modules=['pytest_warnings_report'], + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', + include_package_data=True, + install_requires=load_requirements('requirements/base.in'), + classifiers=[ + 'Development Status :: 4 - Beta', + 'Framework :: Pytest', + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Testing', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 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', + 'Operating System :: OS Independent', + 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)', + ], + entry_points={ + 'pytest11': [ + 'warnings-report = pytest_warnings_report', + ], + }, +) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..bc711e5 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1 @@ +pytest_plugins = 'pytester' diff --git a/tests/test_warnings_report.py b/tests/test_warnings_report.py new file mode 100644 index 0000000..670af53 --- /dev/null +++ b/tests/test_warnings_report.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + + +def test_bar_fixture(testdir): + """Make sure that pytest accepts our fixture.""" + + # create a temporary pytest test module + testdir.makepyfile(""" + def test_sth(bar): + assert bar == "europython2015" + """) + + # run pytest with the following cmd args + result = testdir.runpytest( + '--foo=europython2015', + '-v' + ) + + # fnmatch_lines does an assertion internally + result.stdout.fnmatch_lines([ + '*::test_sth PASSED*', + ]) + + # make sure that that we get a '0' exit code for the testsuite + assert result.ret == 0 + + +def test_help_message(testdir): + result = testdir.runpytest( + '--help', + ) + # fnmatch_lines does an assertion internally + result.stdout.fnmatch_lines([ + 'warnings-report:', + '*--foo=DEST_FOO*Set the value for the fixture "bar".', + ]) + + +def test_hello_ini_setting(testdir): + testdir.makeini(""" + [pytest] + HELLO = world + """) + + testdir.makepyfile(""" + import pytest + + @pytest.fixture + def hello(request): + return request.config.getini('HELLO') + + def test_hello_world(hello): + assert hello == 'world' + """) + + result = testdir.runpytest('-v') + + # fnmatch_lines does an assertion internally + result.stdout.fnmatch_lines([ + '*::test_hello_world PASSED*', + ]) + + # make sure that that we get a '0' exit code for the testsuite + assert result.ret == 0 diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..1823e3c --- /dev/null +++ b/tox.ini @@ -0,0 +1,12 @@ +# For more information about tox, see https://tox.readthedocs.io/en/latest/ +[tox] +envlist = py27,py34,py35,py36,py37,pypy,flake8 + +[testenv] +deps = pytest>=3.0 +commands = pytest {posargs:tests} + +[testenv:flake8] +skip_install = true +deps = flake8 +commands = flake8 pytest_warnings_report.py setup.py tests diff --git a/write_to_html.py b/write_to_html.py new file mode 100644 index 0000000..108ea17 --- /dev/null +++ b/write_to_html.py @@ -0,0 +1,98 @@ +""" +Class used to write pytest warning data into html format +""" +import textwrap +import six + + +class HtmlOutlineWriter(object): + """ + writer to handle html writing + """ + HEAD = textwrap.dedent( + u""" + + + + + + + + """ + ) + + SECTION_START = textwrap.dedent( + u"""\ +
+ + +
+ """ + ) + + SECTION_END = six.u("
") + + def __init__(self, fout): + self.fout = fout + self.section_id = 0 + self.fout.write(self.HEAD) + + def start_section(self, html, klass=None): + self.fout.write( + self.SECTION_START.format(id=self.section_id, html=html, klass=klass or "",) + ) + self.section_id += 1 + + def end_section(self): + self.fout.write(self.SECTION_END) + + def write(self, html): + self.fout.write(html)