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"""\ +