diff --git a/Dockerfile b/Dockerfile index f17e38a..689f471 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,12 @@ -FROM python:3.11.2-slim +FROM python:3.13.5-alpine3.22 -RUN apt-get update \ - && apt-get install curl -y \ - && apt-get remove curl -y \ - && apt-get autoremove -y \ - && rm -rf /var/lib/apt/lists/* +COPY requirements.txt /requirements.txt +COPY dev-requirements.txt /dev-requirements.txt + +RUN pip install -r /requirements.txt -r /dev-requirements.txt -RUN mkdir /opt/analyzer COPY . /opt/analyzer -WORKDIR /opt/analyzer -RUN pip install -r requirements.txt -r dev-requirements.txt -ENTRYPOINT ["/opt/analyzer/bin/run.sh"] +WORKDIR /opt/analyzer +ENTRYPOINT ["sh", "/opt/analyzer/bin/run.sh"] diff --git a/README.md b/README.md index cddd449..8e0d53a 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,24 @@ # Exercism's Python Analyzer -This is Exercism's automated analyzer for the Python track. +This is Exercism's automated analyzer for the Python track exercises. +It is based on and uses [PyLint][pylint-github]. -It is run with `./bin/run.sh $EXERCISM $PATH_TO_FILES $PATH_FOR_OUTPUT` and will read the source code from `$PATH_TO_FILES` and write a text file with an analysis to `$PATH_FOR_OUTPUT`. +It is run from a docker container using `./bin/run-in-docker.sh $EXERCISM $PATH_TO_FILES $PATH_FOR_OUTPUT` and will read the source code from `$PATH_TO_FILES` and write a text file with an analysis to `$PATH_FOR_OUTPUT`. For example: ```bash -./bin/run.sh two_fer ~/solution-238382y7sds7fsadfasj23j/ ~/solution-238382y7sds7fsadfasj23j/output/ +./bin/run-in-docker.sh two_fer ~/solution-238382y7sds7fsadfasj23j/ ~/solution-238382y7sds7fsadfasj23j/output/ ``` -Unit tests can be run from this directory: +Unit tests also require [docker][docker] and can be run locally or from within GitHub via [codespaces][codespaces]: ```bash -pytest -x + +#run from the python-analyzer (project root) directory. +./bin/run-tests-in-docker.sh ``` + +[pylint-github]: https://github.com/pylint-dev/pylint +[docker]: https://www.docker.com/ +[codespaces]: https://github.com/features/codespaces diff --git a/bin/run-in-docker.sh b/bin/run-in-docker.sh index 8de0594..7a33b8e 100755 --- a/bin/run-in-docker.sh +++ b/bin/run-in-docker.sh @@ -34,8 +34,10 @@ mkdir -p "$output_dir" # run image passing the arguments docker run \ + --rm \ + --network none \ + --read-only \ --mount type=bind,src=$PWD/$2,dst=/solution \ --mount type=bind,src=$PWD/$output_dir,dst=/output \ - python-analyzer $1 /solution/ /output/ - - + --mount type=tmpfs,destination=/tmp \ + exercism/python-analyzer $1 /solution/ /output/ diff --git a/bin/run-tests-in-docker.sh b/bin/run-tests-in-docker.sh new file mode 100755 index 0000000..534a23a --- /dev/null +++ b/bin/run-tests-in-docker.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env sh + +# Synopsis: +# Test the test runner Docker image by running it against a predefined set of +# solutions with an expected output. +# The test runner Docker image is built automatically. + +# Output: +# Outputs the diff of the expected test results against the actual test results +# generated by the test runner Docker image. + +# Example: +# ./bin/run-tests-in-docker.sh + +# Stop executing when a command returns a non-zero return code +set -e + +# Build the Docker image +docker build --rm -t exercism/python-analyzer . + +# Run the Docker image using the settings mimicking the production environment +docker run \ + --rm \ + --network none \ + --read-only \ + --mount type=bind,src="${PWD}/test",dst=/opt/analyzer/test \ + --mount type=tmpfs,dst=/tmp \ + --workdir /opt/analyzer \ + --entrypoint pytest \ + exercism/python-analyzer -vv --disable-warnings diff --git a/bin/run.py b/bin/run.py index c5e4307..530a492 100644 --- a/bin/run.py +++ b/bin/run.py @@ -3,6 +3,8 @@ CLI for the auto-analyzer for the Python track on Exercism.org. ./bin/run.sh two_fer ~/solution-238382y7sds7fsadfasj23j/ ~/solution-238382y7sds7fsadfasj23j/output/ """ + + import argparse import importlib.util import sys diff --git a/comments/general/general_recommendations.md b/comments/general/general_recommendations.md new file mode 100644 index 0000000..7536af0 --- /dev/null +++ b/comments/general/general_recommendations.md @@ -0,0 +1,32 @@ +1. Get familiar with the conventions outlined in [PEP 8][pep-8]. + While these are not "law", they are the standard used in the Python project itself and are a great baseline in most coding situations. +2. Read and think about the ideas outlined in [PEP 20 (aka "The Zen of Python")][pep-20]. + Like PEP 8, these are not "laws", but they are solid guiding principles for better and clearer Python code. +3. Prefer clear and easy to follow code over comments. But DO comment where needed for clarity. +4. Consider using type hints to clarify your code. + Explore the type hint [documentation][type-hint-docs] and [why you might not want to type hint][type-hint-nos]. +5. Try to follow the docstring guidelines laid out in [PEP 257][pep-257]. + Good documentation matters. +6. Avoid [magic numbers][magic-numbers]. +7. Prefer [`enumerate()`][enumerate-docs] over [`range(len())`][range-docs] in loops that need both an index and element. +8. Prefer [comprehensions][comprehensions] and [generator expressions][generators] over loops that append to a data structure. + But don't [overuse comprehensions][comprehension-overuse]. +9. When joining more than few substrings or concatenating in a loop, prefer [`str.join()`][join] over other methods of string concatenation. +10. Get familiar with Python's rich set of [built-in functions][built-in-functions] and the [Standard Library][standard-lib]. + Go [here][standard-lib-overview] for a brief tour and some interesting highlights. + +[built-in-functions]: https://docs.python.org/3/library/functions.html +[comprehension-overuse]: https://treyhunner.com/2019/03/abusing-and-overusing-list-comprehensions-in-python/ +[comprehensions]: https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ +[enumerate-docs]: https://docs.python.org/3/library/functions.html#enumerate +[generators]: https://www.pythonmorsels.com/how-write-generator-expression/ +[join]: https://docs.python.org/3/library/stdtypes.html#str.join +[magic-numbers]: https://en.wikipedia.org/wiki/Magic_number_(programming) +[pep-20]: https://peps.python.org/pep-0020/ +[pep-257]: https://peps.python.org/pep-0257/ +[pep-8]: https://peps.python.org/pep-0008/ +[range-docs]: https://docs.python.org/3/library/functions.html#func-range +[standard-lib-overview]: https://docs.python.org/3/tutorial/stdlib.html +[standard-lib]: https://docs.python.org/3/library/index.html +[type-hint-docs]: https://typing.python.org/en/latest/index.html +[type-hint-nos]: https://typing.python.org/en/latest/guides/typing_anti_pitch.html diff --git a/comments/pylint/convention.md b/comments/pylint/convention.md index f3270af..98dd348 100644 --- a/comments/pylint/convention.md +++ b/comments/pylint/convention.md @@ -1,9 +1,15 @@ # pylint convention -**Line %{lineno}** [ _%{code}_ ] : %{message}. - Was reported. +**Line %{lineno} [_%{code}_]** was reported by Pylint: -Which means this code doesn't follow general [`code style`][PEP8] conventions. +%{message}. + +This code doesn't follow general [Python code style][code style] conventions. While this type of issue generally doesn't affect the way code _executes_, it can hurt readability or the performance of automated tools such as documentation generators or test runners. -[PEP8]: https://www.python.org/dev/peps/pep-0008/ +%{bad_code} +%{good_code} +%{related_info} +%{details} + +[code style]: https://www.python.org/dev/peps/pep-0008/ diff --git a/comments/pylint/error.md b/comments/pylint/error.md index 88a9449..735e072 100644 --- a/comments/pylint/error.md +++ b/comments/pylint/error.md @@ -1,6 +1,12 @@ # pylint informational -**Line %{lineno}** [ _%{code}_ ] : %{message}. - Was reported. +**Line %{lineno} [_%{code}_]** was reported by Pylint: + +%{message}. This code has an error or problem that should be addressed. + +%{bad_code} +%{good_code} +%{related_info} +%{details} diff --git a/comments/pylint/fatal.md b/comments/pylint/fatal.md index 4c06874..6cf46b7 100644 --- a/comments/pylint/fatal.md +++ b/comments/pylint/fatal.md @@ -1,8 +1,8 @@ # pylint fatal -**Line %{lineno}** [ _%{code}_ ] : %{message}. - Was reported. +**Line %{lineno} [_%{code}_]** was reported by Pylint: -This is a fatal error. -Something went wrong, and the code cannot be processed any further. +%{message}. + +This is a fatal error. Something went wrong, and the code cannot be processed any further. diff --git a/comments/pylint/informatonal.md b/comments/pylint/informatonal.md index fdf7984..7ba05c2 100644 --- a/comments/pylint/informatonal.md +++ b/comments/pylint/informatonal.md @@ -1,8 +1,13 @@ # pylint informational -**Line %{lineno}** [ _%{code}_ ] : %{message}. - Was reported. +**Line %{lineno} [_%{code}_]** was reported by Pylint: + +%{message}. There is `FIXME`/`TODO`/`XXX` style comment or other "informational" pattern or note in the code. -These tags are often used to annotate places where code is stubbed out but needs work - or to highlight potential design flaws or bugs that need to be addressed in the future. + These tags are often used to annotate places where code is stubbed out but needs work - or to highlight potential design flaws or bugs that need to be addressed in the future. +%{bad_code} +%{good_code} +%{related_info} +%{details} diff --git a/comments/pylint/refactor.md b/comments/pylint/refactor.md index 203e691..8bd403a 100644 --- a/comments/pylint/refactor.md +++ b/comments/pylint/refactor.md @@ -1,10 +1,18 @@ # pylint refactor -**Line %{lineno}** [ _%{code}_ ] : %{message}. - Was reported. +**Line %{lineno} [_%{code}_]** was reported by Pylint: -This code is emitting a [`code smell`][code smell], and may be in need of a rewrite or refactor. -This doesn't mean the code is incorrect or buggy in a _technical sense_ -- only that it appears to have one or more patterns that could lead to _future_ bugs or maintenance issues. +%{message}. + +This code is emitting a [code smell][code smell], and may be in need of +a re-write or refactor. + This doesn't mean the code is incorrect or buggy in a _technical sense_, only that it +appears to have one or more patterns that could lead to _future_ bugs or maintenance issues. Consider taking a closer look at it. +%{bad_code} +%{good_code} +%{related_info} +%{details} + [code smell]: https://en.wikipedia.org/wiki/Code_smell diff --git a/comments/pylint/warning.md b/comments/pylint/warning.md index 18d5e2d..14cdf74 100644 --- a/comments/pylint/warning.md +++ b/comments/pylint/warning.md @@ -1,9 +1,14 @@ # pylint warning -**Line %{lineno}** [ _%{code}_ ] : %{message}. - Was reported. +**Line %{lineno} [_%{code}_]** was reported by Pylint: + +%{message}. There is an issue in the code that could lead to a bug or error in the program. While this error might not be _severe_, it could lead to more severe issues in the future. -It is recommend the problem be addressed before proceeding further. +It is recommended the problem be addressed before proceeding further. +%{bad_code} +%{good_code} +%{related_info} +%{details} diff --git a/dev-requirements.txt b/dev-requirements.txt index e1bd536..83849ed 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,3 @@ -pytest~=7.2.2 -pytest-subtests~=0.10.0 -tomli>=1.1.0; python_full_version < '3.11.2' +black<=25.1.0 +pytest~=8.4.0 +pytest-subtests~=0.14.2 diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/black-jack/analyzer.py b/lib/black-jack/analyzer.py index b8d30ad..416b99c 100644 --- a/lib/black-jack/analyzer.py +++ b/lib/black-jack/analyzer.py @@ -4,8 +4,12 @@ """ import ast + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path -from pylint import epylint as lint +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -15,13 +19,16 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + def analyze(in_path: Path, out_path: Path): - """Analyze the user's Black Jack solution and give feedback. + """ + Analyze the user's Two Fer solution to give feedback. Outputs a JSON that - Outputs a JSON that conforms to: - https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#output-format + conforms to https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#output-format """ # List of Comment objects to process @@ -52,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/card-games/analyzer.py b/lib/card-games/analyzer.py index 6a97442..1b26063 100644 --- a/lib/card-games/analyzer.py +++ b/lib/card-games/analyzer.py @@ -1,23 +1,32 @@ """ Basic analyzer for the `card-games` exercise. -Only Pylint checks are active currently. +Only Pylint checks are currently active. """ + import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes from common.pylint_comments import generate_pylint_comments + class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + def analyze(in_path: Path, out_path: Path): """ - Analyze the user's Two Fer solution to give feedback. Outputs JSON that + Analyze the user's Two Fer solution to give feedback. Outputs a JSON that conforms to https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#output-format """ @@ -47,8 +56,11 @@ def analyze(in_path: Path, out_path: Path): if comments: return Analysis.require(comments).dump(output_file) - # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/cater-waiter/analyzer.py b/lib/cater-waiter/analyzer.py index 96b8ea1..17b6cae 100644 --- a/lib/cater-waiter/analyzer.py +++ b/lib/cater-waiter/analyzer.py @@ -1,19 +1,30 @@ """ -Analyzer for the `cater-waiter` exercise. -Only has Pylint check active currently. +Generic Analyzer for the exercises that don't have a specific +analyzer or specific customizations. + +Only Pylint comments are active. """ + import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes from common.pylint_comments import generate_pylint_comments + class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + def analyze(in_path: Path, out_path: Path): """ @@ -50,4 +61,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/chaitanas-colossal-coaster/analyzer.py b/lib/chaitanas-colossal-coaster/analyzer.py index 2b2d74a..3123782 100644 --- a/lib/chaitanas-colossal-coaster/analyzer.py +++ b/lib/chaitanas-colossal-coaster/analyzer.py @@ -2,18 +2,27 @@ Analyzer for the `chaitanas colossal coaster` exercise. Currently only has pylint checks active. """ + import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes from common.pylint_comments import generate_pylint_comments + class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + def analyze(in_path: Path, out_path: Path): """ @@ -50,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/common/.pylintrc b/lib/common/.pylintrc index e4126cd..927272e 100644 --- a/lib/common/.pylintrc +++ b/lib/common/.pylintrc @@ -1,393 +1,333 @@ -########################################################################### -# This common pylintrc file contains a best-effort configuration to uphold the -# best-practices and style for the Python track exercises of exercism.org. -# -# This file is used when there is no exercise-specific pylintrc. -# When there is an exercise-specific pylintrc, this config file is ignored -# in favor of the exercise-specific config. -# -# As a default, all warnings and checks are "turned on", although that may -# change in the future, as we decide which rules to enforce for all exercises. -# -# **A note on single letter variable names.** -# See this Stack Overflow discussion: -# https://stackoverflow.com/questions/21833872/why-does-pylint-object-to-single-character-variable-names -# for why this is enforced. -########################################################################### - - -[MASTER] - -# Files or directories to be skipped. They should be base names, not paths. -ignore=third_party - -# Files or directories matching the regex patterns are skipped. The regex -# matches against base names, not paths. -ignore-patterns= - -# Pickle collected data for later comparisons. -persistent=no - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= +[MAIN] -# Use multiple processes to speed up Pylint. +analyse-fallback-blocks=no +clear-cache-post-run=yes +extension-pkg-allow-list= +extension-pkg-whitelist= +ignore=third_party jobs=4 - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. +limit-inference-results=100 unsafe-load-any-extension=no +persistent=no +prefer-stubs=no +recursive=no +# Specify a score threshold under which the program will exit with error. +fail-under=10 -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then re-enable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -# inconsistent-return-statements, -# disable=arguments-differ, -# attribute-defined-outside-init, -# duplicate-code, -# filter-builtin-not-iterating, -# fixme, -# global-statement, -# implicit-str-concat-in-sequence, -# import-error, -# import-self, -# input-builtin, -# locally-disabled, -# misplaced-comparison-constant, -# missing-class-docstring, -# missing-function-docstring, -# missing-module-docstring, -# no-absolute-import, -# no-else-break, -# no-else-continue, -# no-else-raise, -# no-else-return, -# no-member, -# no-name-in-module, -# no-self-use, -# raising-string, -# relative-import, -# round-builtin, -# signature-differs, -# suppressed-message, -# too-few-public-methods, -# too-many-ancestors, -# too-many-arguments, -# too-many-boolean-expressions, -# too-many-branches, -# too-many-instance-attributes, -# too-many-locals, -# too-many-nested-blocks, -# too-many-public-methods, -# too-many-return-statements, -# too-many-statements, -# unused-argument, -# unused-import, -# useless-suppression -disable=trailing-whitespace,missing-final-newline - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -# output-format=colorized - -# Tells whether to display a full report or only the messages -reports=no - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins = pylint.extensions.bad_builtin, + # pylint.extensions.code_style, + pylint.extensions.comparison_placement, + pylint.extensions.consider_refactoring_into_while_condition, + # pylint.extensions.docparams, + pylint.extensions.dunder, + pylint.extensions.eq_without_hash, + pylint.extensions.for_any_all, + # pylint.extensions.mccabe, + pylint.extensions.no_self_use, + pylint.extensions.overlapping_exceptions, + pylint.extensions.private_import, + pylint.extensions.redefined_loop_name, + pylint.extensions.set_membership, [BASIC] -# Good variable names which should always be accepted, separated by a comma -good-names=main,_, item, element, index - -# Bad variable names which should always be refused, separated by a comma -bad-names=x,y,i,l,L,O,j,m,n,k - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=y +module-naming-style=snake_case +const-naming-style=UPPER_CASE +variable-naming-style=snake_case +function-naming-style=snake_case +argument-naming-style=snake_case +attr-naming-style=snake_case +class-naming-style=PascalCase +class-attribute-naming-style=any +class-const-naming-style=UPPER_CASE +method-naming-style=snake_case +inlinevar-naming-style=snake_case + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=yes + +# Bad variable names which should always be refused, separated by a comma. +bad-names=x, + y, + i, + l, + a, + b, + j, + o, + z + +# Good variable names which should always be accepted, separated by a comma. +good-names=main, # List of decorators that produce properties, such as abc.abstractproperty. Add # to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl - -# Regular expression matching correct function names -function-rgx=^(?:(?PsetUp|tearDown|setUpModule|tearDownModule)|(?P_?[A-Z][a-zA-Z0-9]*)|(?P_?[a-z][a-z0-9_]*))$ - -# Regular expression matching correct variable names -variable-rgx=^[a-z_][a-z0-9_]{1,30}$ - -# Regular expression matching correct constant names -const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty, + cached_property.cached_property, + cached_property.threaded_cached_property, + cached_property.cached_property_with_ttl, + cached_property.threaded_cached_property_with_ttl -# Regular expression matching correct attribute names -attr-rgx=^_{0,2}[a-z][a-z0-9_]*$ - -# Regular expression matching correct argument names -argument-rgx=^[a-z][a-z0-9_]{1,30}$ +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=5 -# Regular expression matching correct class attribute names -class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs=^.{1,2}$ -# Regular expression matching correct inline iteration names -inlinevar-rgx=^[a-z][a-z0-9_]*$ +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= -# Regular expression matching correct class names -class-rgx=^_?[A-Z][a-zA-Z0-9]*$ -# Regular expression matching correct module names -module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$ +[CLASSES] -# Regular expression matching correct method names -method-rgx=(?x)^(?:(?P_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P_{0,2}[a-z][a-z0-9_]*))$ +check-protected-access-in-special-methods=no +defining-attr-methods=__init__, + __new__, + setUp -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$ +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=10 +valid-classmethod-first-arg=cls, + class_ +valid-metaclass-classmethod-first-arg=mcs -[TYPECHECK] -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager +[DESIGN] -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes +max-args=5 +max-attributes=7 +max-bool-expr=5 +max-branches=12 +max-locals=15 +max-parents=7 +max-positional-arguments=5 +max-public-methods=20 +max-returns=6 +max-statements=50 +min-public-methods=2 -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local +[EXCEPTIONS] -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= +# Exceptions that will emit a warning when caught. +overgeneral-exceptions=StandardError, + Exception, + BaseException [FORMAT] +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + # Maximum number of characters on a single line. max-line-length=120 -# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt -# lines made too long by directives to pytype. - # Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=(?x)( - ^\s*(\#\ )??$| - ^\s*(from\s+\S+\s+)?import\s+.+$) - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=yes +ignore-long-lines=(?x)(^\s*(\#\ )??$|^\s*(from\s+\S+\s+)?import\s+.+$) -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -# As of Pylint 2.6+, this option has been disabled, so this is commented out. -# no-space-check= - -# Maximum number of lines in a module +# Maximum number of lines in a module. max-module-lines=99999 -# String used as indentation unit. Currently 4, consistent with -# PEP 8. -indent-string=' ' - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 +single-line-class-stmt=no +single-line-if-stmt=yes -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= +[IMPORTS] -[MISCELLANEOUS] +# Allow explicit reexports by alias from a package __init__. +allow-reexport-from-package=no -# List of note tags to take in consideration, separated by a comma. -notes=TODO +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules=regsub, + TERMIOS, + Bastion, + rexec, + sets -[STRING] +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant, + absl -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=yes +[LOGGING] +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old -[VARIABLES] +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging,absl.logging -# Tells whether we should check for unused import in __init__ files. -init-import=no -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) +[MESSAGES CONTROL] -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, +# UNDEFINED. +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED + +# Disable the message, report, category or checker with the given id(s). +disable=raw-checker-failed, + locally-disabled, + suppressed-message, + arguments-differ, + fixme, + line-too-long, + global-statement, + import-error, + no-member, + signature-differs, + too-many-locals, + too-many-public-methods, + too-many-return-statements, + too-many-statements, + unnecessary-pass + +# Include some helpful details on errors messages for naming rules: +include-naming-hint = yes + + +[METHOD_ARGS] + +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods=requests.api.delete, + requests.api.get, + requests.api.head, + requests.api.options, + requests.api.patch, + requests.api.post, + requests.api.put, + requests.api.request -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks= -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools +[MISCELLANEOUS] +check-fixme-in-docstring=no +notes=TODO -[LOGGING] -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging,absl.logging +[REFACTORING] +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 -[SIMILARITIES] +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error -# Minimum lines number of a similarity. -min-similarity-lines=4 +# Let 'consider-using-join' be raised when the separator to join on would be +# non-empty (resulting in expected fixes of the type: ``"- " + " - +# ".join(items)``) +suggest-join-with-non-empty-separator=yes -# Ignore comments when computing similarities. -ignore-comments=yes -# Ignore docstrings when computing similarities. -ignore-docstrings=yes +[REPORTS] -# Ignore imports when computing similarities. -ignore-imports=no +reports=no [SPELLING] -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 -# List of comma separated words that should not be checked. -spelling-ignore-words= +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. spelling-store-unknown-words=no -[IMPORTS] +[STRING] -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub, - TERMIOS, - Bastion, - rexec, - sets +check-quote-consistency=yes +check-str-concat-over-line-jumps=yes -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= +[VARIABLES] -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= +allow-global-unused-variables=yes +dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) +ignored-argument-names=_.*|^ignored_|^unused_ +redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= +# Tells whether we should check for unused import in __init__ files. +init-import=no -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant, absl -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no +[TYPECHECK] +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager -[CLASSES] +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls, - class_ +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes -[EXCEPTIONS] +# The maximum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=StandardError, - Exception, - BaseException +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx=.*[Mm]ixin + +# List of decorators that change the signature of a decorated function. +signature-mutators= diff --git a/lib/common/comment.py b/lib/common/comment.py index 4c97a55..0af3a65 100644 --- a/lib/common/comment.py +++ b/lib/common/comment.py @@ -1,11 +1,12 @@ """ Classes for working with comments and comment types. """ + + from enum import Enum, unique from dataclasses import dataclass, field, asdict - @unique class BaseFeedback(Enum): """ @@ -51,7 +52,6 @@ class Comment: - @unique class Summary(Enum): """ @@ -60,9 +60,9 @@ class Summary(Enum): CELEBRATE = "🎉 Congratulations! This solution is very close to ideal! We don't have any specific recommendations." REQUIRE = "There are a few changes we'd like you to make before completing this exercise." - DIRECT = "There are a few changes we suggest that can bring your solution closer to ideal." + DIRECT = "There are a few suggested changes that can bring your solution closer to ideal." INFORM = "Good work! 🌟 Here are some general recommendations for improving your Python code." - GENERIC = "We don't have a custom analysis for this exercise yet, but here are some comments from PyLint to help you improve your code." + GENERIC = "We don't have a custom analysis for this exercise yet, but here are some general recommendations to improve your Python code." def __str__(self): return self.value diff --git a/lib/common/exercise-names.txt b/lib/common/exercise-names.txt new file mode 100644 index 0000000..ba38015 --- /dev/null +++ b/lib/common/exercise-names.txt @@ -0,0 +1,163 @@ +accumulate +acronym +affine-cipher +all-your-base +allergies +alphametics +anagram +armstrong-numbers +atbash-cipher +bank-account +beer-song +binary +binary-search +binary-search-tree +black-jack +bob +book-store +bottle-song +bowling +camicia +card-games +cater-waiter +chaitanas-colossal-coaster +change +circular-buffer +clock +collatz-conjecture +common +complex-numbers +connect +crypto-square +currency-exchange +custom-set +darts +diamond +difference-of-squares +diffie-hellman +dnd-character +dominoes +dot-dsl +electric-bill +eliuds-eggs +ellens-alien-game +error-handling +etl +flatten-array +flower-field +food-chain +forth +game-of-life +ghost-gobble-arcade-game +gigasecond +go-counting +grade-school +grains +grep +guidos-gorgeous-lasagna +hamming +hangman +hexadecimal +high-scores +house +inventory-management +isbn-verifier +isogram +killer-sudoku-helper +kindergarten-garden +knapsack +largest-series-product +leap +ledger +line-up +linked-list +list-ops +little-sisters-essay +little-sisters-vocab +locomotive-engineer +log-levels +luhn +making-the-grade +markdown +matching-brackets +matrix +mecha-munch-management +meetup +meltdown-mitigation +minesweeper +nth-prime +ocr-numbers +octal +paasio +palindrome-products +pangram +pascals-triangle +perfect-numbers +phone-number +pig-latin +plane-tickets +point-mutations +poker +pop-count +pov +pretty-leaflet +prime-factors +processing-logs +protein-translation +proverb +pythagorean-triplet +queen-attack +rail-fence-cipher +raindrops +rational-numbers +react +rectangles +relative-distance +resistor-color +resistor-color-duo +resistor-color-expert +resistor-color-trio +rest-api +restaurant-rozalynn +reverse-string +rna-transcription +robot-name +robot-simulator +roman-numerals +rotational-cipher +run-length-encoding +saddle-points +satellite +say +scale-generator +scrabble-score +secret-handshake +series +sgf-parsing +sieve +simple-cipher +simple-linked-list +space-age +spiral-matrix +square-root +state-of-tic-tac-toe +strain +sublist +sum-of-multiples +swift-scheduling +tisbury-treasure-hunt +tournament +transpose +tree-building +triangle +trinary +twelve-days +two-bucket +two-fer +variable-length-quantity +word-count +word-search +wordy +yacht +zebra-puzzle +zipper diff --git a/lib/common/exercise.py b/lib/common/exercise.py index 34e9a8c..21b9374 100644 --- a/lib/common/exercise.py +++ b/lib/common/exercise.py @@ -1,6 +1,5 @@ -""" -Helpers for exercise discovery and execution. -""" +"""Helpers for exercise discovery and execution.""" + import sys import importlib import json @@ -10,8 +9,18 @@ ROOT = Path(__file__).resolve(strict=True).parent LIBRARY = ROOT.parent.resolve(strict=True) -# map each available exercise name to its EXERCISE/anaylzer.py module -ANALYZERS = {f.parent.name: f for f in LIBRARY.glob("*/analyzer.py")} +# Map each available exercise name to its EXERCISE/analyzer.py module. +# Use the analyzer at /opt/analyzer/lib/generic/analyzer.py if +# no specific one is found in the directory. +ANALYZERS = {analyzer.parent.name: analyzer for analyzer in LIBRARY.glob("*/analyzer.py")} + +file_path = Path('/opt/analyzer/lib/common/exercise-names.txt') + +with open(file_path, 'r') as file: + content = file.read().splitlines() + +for exercise in content: + ANALYZERS.setdefault(exercise, Path('/opt/analyzer/lib/common/generic_analyzer/analyzer.py')) class ExerciseError(Exception): diff --git a/lib/common/generic_analyzer/analyzer.py b/lib/common/generic_analyzer/analyzer.py new file mode 100644 index 0000000..17b6cae --- /dev/null +++ b/lib/common/generic_analyzer/analyzer.py @@ -0,0 +1,68 @@ +""" +Generic Analyzer for the exercises that don't have a specific +analyzer or specific customizations. + +Only Pylint comments are active. +""" + +import ast + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint +from pathlib import Path +from pylint.reporters.text import TextReporter + +from common import Analysis, BaseFeedback, Summary +from common.comment import Comment, CommentTypes +from common.pylint_comments import generate_pylint_comments + + +class Comments(BaseFeedback): + NO_MODULE = ("general", "no_module") + NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") + MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + + +def analyze(in_path: Path, out_path: Path): + """ + Analyze the user's Two Fer solution to give feedback. Outputs a JSON that + + conforms to https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#output-format + """ + + # List of Comment objects to process + comments = [] + + output_file = out_path.parent.joinpath("analysis.json") + + # input file - if it can't be found, fail and bail + try: + user_solution = in_path.read_text() + except OSError as err: + # fail out fast with an ESSENTIAL (required) type comment for the student + comments.append(Comment(type=CommentTypes.ESSENTIAL, params={}, comment=Comments.MALFORMED_CODE)) + finally: + if comments: + return Analysis.require(comments).dump(output_file) + + # AST - if an AST can't be made, fail and bail + try: + tree = ast.parse(user_solution) + except Exception: + # If ast.parse fails, assume malformed code and fail with an ESSENTIAL (required) type comment for the student + comments.append(Comment(type=CommentTypes.ESSENTIAL, params={}, comment=Comments.MALFORMED_CODE)) + finally: + if comments: + return Analysis.require(comments).dump(output_file) + + # Generate PyLint comments for additional feedback. + comments.extend(generate_pylint_comments(in_path)) + + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/common/pylint_comments.py b/lib/common/pylint_comments.py index 7595d41..1783952 100644 --- a/lib/common/pylint_comments.py +++ b/lib/common/pylint_comments.py @@ -1,6 +1,25 @@ +"""Functions for providing PyLint feedback..""" + from common.comment import Comment, CommentTypes -from pylint import epylint as lint +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter + + +def find_pylint_rule_details(path): + """Find Pylint extended feedback by rule.""" + + file_path = Path(path) + + try: + with open(file_path, 'r') as file: + content = file.read() + except FileNotFoundError: + return None + + return content def generate_pylint_comments(in_path, pylint_spec='/opt/analyzer/lib/common/.pylintrc'): @@ -9,12 +28,6 @@ def generate_pylint_comments(in_path, pylint_spec='/opt/analyzer/lib/common/.pyl e.g. if code follows PEP8 Style Convention """ - template = '--msg-template="{category}, {line}, {msg_id} {symbol}, {msg}"' - rcfile = f'--rcfile={pylint_spec}' - cmnd_line_options = f" {rcfile} --score=no {template}" - - pylint_stdout, _ = lint.py_run(str(in_path) + cmnd_line_options, return_std=True) - status_mapping = { 'informational': CommentTypes.INFORMATIVE, 'refactor' : CommentTypes.ACTIONABLE, @@ -24,27 +37,48 @@ def generate_pylint_comments(in_path, pylint_spec='/opt/analyzer/lib/common/.pyl 'fatal' : CommentTypes.ESSENTIAL } - cleaned_pylint_output = [tuple(item.strip('" ').split(', ')) - for item in pylint_stdout.getvalue().splitlines() - if '**' not in item] + pylint_output = StringIO() + reporter = TextReporter(pylint_output) + template = '--msg-template="{category}, {line}, {msg_id} {symbol}, {msg}"' + rcfile = f'--rcfile={pylint_spec}' + cmnd_line_options = [f"{str(in_path)}", rcfile, "--score=n", f"{template}"] + messages_path = '/opt/analyzer/lib/common/pylint_data/messages' + + Run(cmnd_line_options, reporter=reporter, exit=False) + + cleaned_pylint_output = (tuple(item.strip('" ').split(', ')) + for item in pylint_output.getvalue().splitlines() + if '**' not in item) pylint_comments = [] for line in cleaned_pylint_output: if line[0]: - if line[2] == "C0301 line-too-long": + rule_name = line[2][6:] + bad = find_pylint_rule_details(path=f"{messages_path}/{rule_name}/bad.py") + good = find_pylint_rule_details(path=f"{messages_path}/{rule_name}/good.py") + related = find_pylint_rule_details(path=f"{messages_path}/{rule_name}/related.md") + details = find_pylint_rule_details(path=f"{messages_path}/{rule_name}/details.md") + + if not line[2] or line[2] == "C0301 line-too-long": continue - if line[2] in ("C0114 missing-module-docstring", + if line[2] in {"C0114 missing-module-docstring", "C0116 missing-function-docstring", - "C0304 missing-final-newline"): + "C0304 missing-final-newline"}: - pylint_comments.append(Comment(type=status_mapping['informational'], - params={'lineno': line[1], 'code': line[2], 'message': line[3:]}, - comment=f'python.pylint.{line[0]}')) + status_type = status_mapping['informational'] else: - pylint_comments.append(Comment(type=status_mapping[line[0]], - params={'lineno': line[1], 'code': line[2], 'message': line[3:]}, - comment=f'python.pylint.{line[0]}')) + status_type = status_mapping[line[0]] + + pylint_comments.append(Comment(type=status_type, + params={'lineno': line[1], + 'code': line[2], + 'message': ', '.join(line[3:]), + 'bad_code': f'Instead of: \n```python\n{bad}```\n\n' if bad else None, + 'good_code': f'Try: \n```python\n{good}```\n\n' if good else None, + 'related_info': related, + 'details': details}, + comment=f'python.pylint.{line[0]}')) return pylint_comments diff --git a/lib/common/pylint_data/messages/abstract-class-instantiated/bad.py b/lib/common/pylint_data/messages/abstract-class-instantiated/bad.py new file mode 100644 index 0000000..f514a5c --- /dev/null +++ b/lib/common/pylint_data/messages/abstract-class-instantiated/bad.py @@ -0,0 +1,10 @@ +import abc + + +class Animal(abc.ABC): + @abc.abstractmethod + def make_sound(self): + pass + + +sheep = Animal() # [abstract-class-instantiated] diff --git a/lib/common/pylint_data/messages/abstract-class-instantiated/good.py b/lib/common/pylint_data/messages/abstract-class-instantiated/good.py new file mode 100644 index 0000000..c9dbce1 --- /dev/null +++ b/lib/common/pylint_data/messages/abstract-class-instantiated/good.py @@ -0,0 +1,15 @@ +import abc + + +class Animal(abc.ABC): + @abc.abstractmethod + def make_sound(self): + pass + + +class Sheep(Animal): + def make_sound(self): + print("bhaaaaa") + + +sheep = Sheep() diff --git a/lib/common/pylint_data/messages/abstract-method/bad.py b/lib/common/pylint_data/messages/abstract-method/bad.py new file mode 100644 index 0000000..81e59e7 --- /dev/null +++ b/lib/common/pylint_data/messages/abstract-method/bad.py @@ -0,0 +1,20 @@ +import abc + + +class WildAnimal: + @abc.abstractmethod + def make_sound(self): + pass + + +class Panther(WildAnimal): # [abstract-method] + pass + + +class Pet: + def make_sound(self): + raise NotImplementedError + + +class Cat(Pet): # [abstract-method] + pass \ No newline at end of file diff --git a/lib/common/pylint_data/messages/abstract-method/good.py b/lib/common/pylint_data/messages/abstract-method/good.py new file mode 100644 index 0000000..8a088cb --- /dev/null +++ b/lib/common/pylint_data/messages/abstract-method/good.py @@ -0,0 +1,22 @@ +import abc + + +class WildAnimal: + @abc.abstractmethod + def make_sound(self): + pass + + +class Panther(WildAnimal): + def make_sound(self): + print("MEEEOW") + + +class Pet: + def make_sound(self): + raise NotImplementedError + + +class Cat(Pet): + def make_sound(self): + print("Meeeow") \ No newline at end of file diff --git a/lib/common/pylint_data/messages/access-member-before-definition/bad.py b/lib/common/pylint_data/messages/access-member-before-definition/bad.py new file mode 100644 index 0000000..64f349f --- /dev/null +++ b/lib/common/pylint_data/messages/access-member-before-definition/bad.py @@ -0,0 +1,5 @@ +class Unicorn: + def __init__(self, fluffiness_level): + if self.fluffiness_level > 9000: # [access-member-before-definition] + print("It's OVER-FLUFFYYYY ! *crush glasses*") + self.fluffiness_level = fluffiness_level diff --git a/lib/common/pylint_data/messages/access-member-before-definition/good.py b/lib/common/pylint_data/messages/access-member-before-definition/good.py new file mode 100644 index 0000000..78d59fa --- /dev/null +++ b/lib/common/pylint_data/messages/access-member-before-definition/good.py @@ -0,0 +1,5 @@ +class Unicorn: + def __init__(self, fluffiness_level): + self.fluffiness_level = fluffiness_level + if self.fluffiness_level > 9000: + print("It's OVER-FLUFFYYYY ! *crush glasses*") diff --git a/lib/common/pylint_data/messages/anomalous-backslash-in-string/bad.py b/lib/common/pylint_data/messages/anomalous-backslash-in-string/bad.py new file mode 100644 index 0000000..32da7dd --- /dev/null +++ b/lib/common/pylint_data/messages/anomalous-backslash-in-string/bad.py @@ -0,0 +1 @@ +string = "\z" # [syntax-error] diff --git a/lib/common/pylint_data/messages/anomalous-backslash-in-string/details.md b/lib/common/pylint_data/messages/anomalous-backslash-in-string/details.md new file mode 100644 index 0000000..22bed5b --- /dev/null +++ b/lib/common/pylint_data/messages/anomalous-backslash-in-string/details.md @@ -0,0 +1,6 @@ +`\z` is same as `\\z` because there is no escape sequence for `z`. +But that is not clear for the reader of the code. + +The only reason this is demonstrated to raise `syntax-error` is because pylint's CI now runs on Python 3.12, where this truly raises a `SyntaxError`. +We hope to address this discrepancy in the documentation in the future. + diff --git a/lib/common/pylint_data/messages/anomalous-backslash-in-string/good.py b/lib/common/pylint_data/messages/anomalous-backslash-in-string/good.py new file mode 100644 index 0000000..9c876f1 --- /dev/null +++ b/lib/common/pylint_data/messages/anomalous-backslash-in-string/good.py @@ -0,0 +1,5 @@ +string = "\\z" + +string = "\t" + +string = r"\z" diff --git a/lib/common/pylint_data/messages/anomalous-backslash-in-string/related.md b/lib/common/pylint_data/messages/anomalous-backslash-in-string/related.md new file mode 100644 index 0000000..998a55c --- /dev/null +++ b/lib/common/pylint_data/messages/anomalous-backslash-in-string/related.md @@ -0,0 +1,2 @@ +- [String and Bytes literals](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals)_ +- [Long form stackoverflow explanation](https://stackoverflow.com/a/19030982/2519059>) diff --git a/lib/common/pylint_data/messages/anomalous-unicode-escape-in-string/bad.py b/lib/common/pylint_data/messages/anomalous-unicode-escape-in-string/bad.py new file mode 100644 index 0000000..21d25ea --- /dev/null +++ b/lib/common/pylint_data/messages/anomalous-unicode-escape-in-string/bad.py @@ -0,0 +1 @@ +print(b"\u%b" % b"0394") # [syntax-error] diff --git a/lib/common/pylint_data/messages/anomalous-unicode-escape-in-string/good.py b/lib/common/pylint_data/messages/anomalous-unicode-escape-in-string/good.py new file mode 100644 index 0000000..c5f4cf4 --- /dev/null +++ b/lib/common/pylint_data/messages/anomalous-unicode-escape-in-string/good.py @@ -0,0 +1 @@ +print(b"\\u%b" % b"0394") diff --git a/lib/common/pylint_data/messages/arguments-differ/bad.py b/lib/common/pylint_data/messages/arguments-differ/bad.py new file mode 100644 index 0000000..489ac9d --- /dev/null +++ b/lib/common/pylint_data/messages/arguments-differ/bad.py @@ -0,0 +1,8 @@ +class Drink: + def mix(self, fluid_one, fluid_two): + return fluid_one + fluid_two + + +class Cocktail(Drink): + def mix(self, fluid_one, fluid_two, alcoholic_fluid): # [arguments-differ] + return fluid_one + fluid_two + alcoholic_fluid diff --git a/lib/common/pylint_data/messages/arguments-differ/details.md b/lib/common/pylint_data/messages/arguments-differ/details.md new file mode 100644 index 0000000..0b1a7dd --- /dev/null +++ b/lib/common/pylint_data/messages/arguments-differ/details.md @@ -0,0 +1,10 @@ +`argument-differ` denotes an issue with the [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle). +This means that the code in question violates an important OOP design principle which does not have one single solution. +We recommend searching online for the best solution in your case. + +To give some examples of potential solutions: + +* Add the argument to the parent class +* Remove the inheritance completely +* Add default arguments to the child class + diff --git a/lib/common/pylint_data/messages/arguments-differ/good.py b/lib/common/pylint_data/messages/arguments-differ/good.py new file mode 100644 index 0000000..d403621 --- /dev/null +++ b/lib/common/pylint_data/messages/arguments-differ/good.py @@ -0,0 +1,31 @@ +""" +Here we assume that 'Drink' and 'Cocktail' are different things and should +not be treated together like if they were the same thing. +""" + + +class Drink: + def mix(self, fluid_one, fluid_two): + return fluid_one + fluid_two + + +class Cocktail: + def mix(self, fluid_one, fluid_two, alcoholic_fluid): + return fluid_one + fluid_two + alcoholic_fluid + + +""" +Here we assume that drink and cocktail are the same thing and should actually +inherit from each over. We also assume that any Cocktail can be treated like +a Drink (if you add beer to it). +""" + + +class Drink: + def mix(self, fluid_one, fluid_two): + return fluid_one + fluid_two + + +class Cocktail(Drink): + def mix(self, fluid_one, fluid_two, alcoholic_fluid="Beer"): + return fluid_one + fluid_two + alcoholic_fluid \ No newline at end of file diff --git a/lib/common/pylint_data/messages/arguments-out-of-order/bad.py b/lib/common/pylint_data/messages/arguments-out-of-order/bad.py new file mode 100644 index 0000000..06f6896 --- /dev/null +++ b/lib/common/pylint_data/messages/arguments-out-of-order/bad.py @@ -0,0 +1,13 @@ +def function_3_args(first_argument, second_argument, third_argument): + """Three arguments function""" + return first_argument, second_argument, third_argument + + +def args_out_of_order(): + first_argument = 1 + second_argument = 2 + third_argument = 3 + + function_3_args( # [arguments-out-of-order] + first_argument, third_argument, second_argument + ) diff --git a/lib/common/pylint_data/messages/arguments-out-of-order/good.py b/lib/common/pylint_data/messages/arguments-out-of-order/good.py new file mode 100644 index 0000000..06ce726 --- /dev/null +++ b/lib/common/pylint_data/messages/arguments-out-of-order/good.py @@ -0,0 +1,11 @@ +def function_3_args(first_argument, second_argument, third_argument): + """Three arguments function""" + return first_argument, second_argument, third_argument + + +def args_out_of_order(): + first_argument = 1 + second_argument = 2 + third_argument = 3 + + function_3_args(first_argument, second_argument, third_argument) diff --git a/lib/common/pylint_data/messages/arguments-renamed/bad.py b/lib/common/pylint_data/messages/arguments-renamed/bad.py new file mode 100644 index 0000000..98f53a1 --- /dev/null +++ b/lib/common/pylint_data/messages/arguments-renamed/bad.py @@ -0,0 +1,15 @@ +class Fruit: + def brew(self, ingredient_name: str): + print(f"Brewing a {type(self)} with {ingredient_name}") + + +class Apple(Fruit): ... + + +class Orange(Fruit): + def brew(self, flavor: str): # [arguments-renamed] + print(f"Brewing an orange with {flavor}") + + +for fruit, ingredient_name in [[Orange(), "thyme"], [Apple(), "cinnamon"]]: + fruit.brew(ingredient_name=ingredient_name) diff --git a/lib/common/pylint_data/messages/arguments-renamed/good.py b/lib/common/pylint_data/messages/arguments-renamed/good.py new file mode 100644 index 0000000..e150d65 --- /dev/null +++ b/lib/common/pylint_data/messages/arguments-renamed/good.py @@ -0,0 +1,15 @@ +class Fruit: + def brew(self, ingredient_name: str): + print(f"Brewing a {type(self)} with {ingredient_name}") + + +class Apple(Fruit): ... + + +class Orange(Fruit): + def brew(self, ingredient_name: str): + print(f"Brewing an orange with {ingredient_name}") + + +for fruit, ingredient_name in [[Orange(), "thyme"], [Apple(), "cinnamon"]]: + fruit.brew(ingredient_name=ingredient_name) diff --git a/lib/common/pylint_data/messages/assert-on-string-literal/bad.py b/lib/common/pylint_data/messages/assert-on-string-literal/bad.py new file mode 100644 index 0000000..adbe2a4 --- /dev/null +++ b/lib/common/pylint_data/messages/assert-on-string-literal/bad.py @@ -0,0 +1,3 @@ +def test_division(): + a = 9 / 3 + assert "No ZeroDivisionError were raised" # [assert-on-string-literal] diff --git a/lib/common/pylint_data/messages/assert-on-string-literal/details.md b/lib/common/pylint_data/messages/assert-on-string-literal/details.md new file mode 100644 index 0000000..597da7e --- /dev/null +++ b/lib/common/pylint_data/messages/assert-on-string-literal/details.md @@ -0,0 +1,4 @@ +Directly asserting a string literal will always pass. +The fix is to test something that could fail, or not assert at all. + +For `unittest` assertions there is the similar `redundant-unittest-assert` message. diff --git a/lib/common/pylint_data/messages/assert-on-string-literal/good.py b/lib/common/pylint_data/messages/assert-on-string-literal/good.py new file mode 100644 index 0000000..ba27075 --- /dev/null +++ b/lib/common/pylint_data/messages/assert-on-string-literal/good.py @@ -0,0 +1,3 @@ +def test_division(): + a = 9 / 3 + assert a == 3 diff --git a/lib/common/pylint_data/messages/assert-on-string-literal/related.md b/lib/common/pylint_data/messages/assert-on-string-literal/related.md new file mode 100644 index 0000000..0679317 --- /dev/null +++ b/lib/common/pylint_data/messages/assert-on-string-literal/related.md @@ -0,0 +1,4 @@ +- [Tests without assertion](https://stackoverflow.com/a/137418/2519059)_ +- [Testing that there is no error raised](https://stackoverflow.com/questions/20274987) +- [Parametrizing conditional raising](https://docs.pytest.org/en/latest/example/parametrize.html#parametrizing-conditional-raising) + diff --git a/lib/common/pylint_data/messages/assert-on-tuple/bad.py b/lib/common/pylint_data/messages/assert-on-tuple/bad.py new file mode 100644 index 0000000..ffcc34c --- /dev/null +++ b/lib/common/pylint_data/messages/assert-on-tuple/bad.py @@ -0,0 +1 @@ +assert (1, None) # [assert-on-tuple] diff --git a/lib/common/pylint_data/messages/assert-on-tuple/details.md b/lib/common/pylint_data/messages/assert-on-tuple/details.md new file mode 100644 index 0000000..41abd8a --- /dev/null +++ b/lib/common/pylint_data/messages/assert-on-tuple/details.md @@ -0,0 +1,4 @@ +Directly asserting a non-empty tuple will always pass. +The solution is to test something that could fail, or not assert at all. + +For `unittest` assertions there is the similar `redundant-unittest-assert` message. diff --git a/lib/common/pylint_data/messages/assert-on-tuple/good.py b/lib/common/pylint_data/messages/assert-on-tuple/good.py new file mode 100644 index 0000000..cea57cf --- /dev/null +++ b/lib/common/pylint_data/messages/assert-on-tuple/good.py @@ -0,0 +1,3 @@ +x, y = (1, None) +assert x +assert y diff --git a/lib/common/pylint_data/messages/assigning-non-slot/bad.py b/lib/common/pylint_data/messages/assigning-non-slot/bad.py new file mode 100644 index 0000000..506ad42 --- /dev/null +++ b/lib/common/pylint_data/messages/assigning-non-slot/bad.py @@ -0,0 +1,10 @@ +class Student: + __slots__ = ("name",) + + def __init__(self, name, surname): + self.name = name + self.surname = surname # [assigning-non-slot] + self.setup() + + def setup(self): + pass diff --git a/lib/common/pylint_data/messages/assigning-non-slot/good.py b/lib/common/pylint_data/messages/assigning-non-slot/good.py new file mode 100644 index 0000000..7337286 --- /dev/null +++ b/lib/common/pylint_data/messages/assigning-non-slot/good.py @@ -0,0 +1,10 @@ +class Student: + __slots__ = ("name", "surname") + + def __init__(self, name, surname): + self.name = name + self.surname = surname + self.setup() + + def setup(self): + pass diff --git a/lib/common/pylint_data/messages/assignment-from-no-return/bad.py b/lib/common/pylint_data/messages/assignment-from-no-return/bad.py new file mode 100644 index 0000000..ea3822a --- /dev/null +++ b/lib/common/pylint_data/messages/assignment-from-no-return/bad.py @@ -0,0 +1,5 @@ +def add(x, y): + print(x + y) + + +value = add(10, 10) # [assignment-from-no-return] diff --git a/lib/common/pylint_data/messages/assignment-from-no-return/good.py b/lib/common/pylint_data/messages/assignment-from-no-return/good.py new file mode 100644 index 0000000..c019007 --- /dev/null +++ b/lib/common/pylint_data/messages/assignment-from-no-return/good.py @@ -0,0 +1,5 @@ +def add(x, y): + return x + y + + +value = add(10, 10) diff --git a/lib/common/pylint_data/messages/assignment-from-none/bad.py b/lib/common/pylint_data/messages/assignment-from-none/bad.py new file mode 100644 index 0000000..033bd89 --- /dev/null +++ b/lib/common/pylint_data/messages/assignment-from-none/bad.py @@ -0,0 +1,5 @@ +def function(): + return None + + +f = function() # [assignment-from-none] diff --git a/lib/common/pylint_data/messages/assignment-from-none/good.py b/lib/common/pylint_data/messages/assignment-from-none/good.py new file mode 100644 index 0000000..d2f4cbc --- /dev/null +++ b/lib/common/pylint_data/messages/assignment-from-none/good.py @@ -0,0 +1,5 @@ +def function(): + return None + + +f = function() if function() else 1 diff --git a/lib/common/pylint_data/messages/astroid-error/details.md b/lib/common/pylint_data/messages/astroid-error/details.md new file mode 100644 index 0000000..5daeea3 --- /dev/null +++ b/lib/common/pylint_data/messages/astroid-error/details.md @@ -0,0 +1,3 @@ +This is a message linked to an internal problem in pylint. There\'s +nothing to change in your code, but maybe in pylint\'s configuration or +installation. diff --git a/lib/common/pylint_data/messages/async-context-manager-with-regular-with/bad.py b/lib/common/pylint_data/messages/async-context-manager-with-regular-with/bad.py new file mode 100644 index 0000000..72b5201 --- /dev/null +++ b/lib/common/pylint_data/messages/async-context-manager-with-regular-with/bad.py @@ -0,0 +1,10 @@ +from contextlib import asynccontextmanager + + +@asynccontextmanager +async def async_context(): + yield + + +with async_context(): # [async-context-manager-with-regular-with] + print("This will cause an error at runtime") diff --git a/lib/common/pylint_data/messages/async-context-manager-with-regular-with/good.py b/lib/common/pylint_data/messages/async-context-manager-with-regular-with/good.py new file mode 100644 index 0000000..f9882a4 --- /dev/null +++ b/lib/common/pylint_data/messages/async-context-manager-with-regular-with/good.py @@ -0,0 +1,12 @@ +import asyncio +from contextlib import asynccontextmanager + + +@asynccontextmanager +async def async_context(): + yield + + +async def main(): + async with async_context(): + print("This works correctly") diff --git a/lib/common/pylint_data/messages/async-context-manager-with-regular-with/related.md b/lib/common/pylint_data/messages/async-context-manager-with-regular-with/related.md new file mode 100644 index 0000000..7cb77bc --- /dev/null +++ b/lib/common/pylint_data/messages/async-context-manager-with-regular-with/related.md @@ -0,0 +1,3 @@ +- [PEP 492 - Coroutines with async and await syntax](https://peps.python.org/pep-0492/) +- [contextlib.asynccontextmanager](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager) + diff --git a/lib/common/pylint_data/messages/attribute-defined-outside-init/bad.py b/lib/common/pylint_data/messages/attribute-defined-outside-init/bad.py new file mode 100644 index 0000000..e62884d --- /dev/null +++ b/lib/common/pylint_data/messages/attribute-defined-outside-init/bad.py @@ -0,0 +1,3 @@ +class Student: + def register(self): + self.is_registered = True # [attribute-defined-outside-init] diff --git a/lib/common/pylint_data/messages/attribute-defined-outside-init/good.py b/lib/common/pylint_data/messages/attribute-defined-outside-init/good.py new file mode 100644 index 0000000..cc7d7ac --- /dev/null +++ b/lib/common/pylint_data/messages/attribute-defined-outside-init/good.py @@ -0,0 +1,6 @@ +class Student: + def __init__(self): + self.is_registered = False + + def register(self): + self.is_registered = True diff --git a/lib/common/pylint_data/messages/await-outside-async/bad.py b/lib/common/pylint_data/messages/await-outside-async/bad.py new file mode 100644 index 0000000..35fd8c3 --- /dev/null +++ b/lib/common/pylint_data/messages/await-outside-async/bad.py @@ -0,0 +1,5 @@ +import asyncio + + +def main(): + await asyncio.sleep(1) # [await-outside-async] diff --git a/lib/common/pylint_data/messages/await-outside-async/good.py b/lib/common/pylint_data/messages/await-outside-async/good.py new file mode 100644 index 0000000..231794b --- /dev/null +++ b/lib/common/pylint_data/messages/await-outside-async/good.py @@ -0,0 +1,5 @@ +import asyncio + + +async def main(): + await asyncio.sleep(1) diff --git a/lib/common/pylint_data/messages/await-outside-async/related.md b/lib/common/pylint_data/messages/await-outside-async/related.md new file mode 100644 index 0000000..519d599 --- /dev/null +++ b/lib/common/pylint_data/messages/await-outside-async/related.md @@ -0,0 +1 @@ +- [PEP 492](https://peps.python.org/pep-0492/#await-expression) diff --git a/lib/common/pylint_data/messages/bad-builtin/bad.py b/lib/common/pylint_data/messages/bad-builtin/bad.py new file mode 100644 index 0000000..1c60815 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-builtin/bad.py @@ -0,0 +1,2 @@ +numbers = list(map(lambda x: 2 * x, [1, 2, 3])) # [bad-builtin] +print(numbers) diff --git a/lib/common/pylint_data/messages/bad-builtin/good.py b/lib/common/pylint_data/messages/bad-builtin/good.py new file mode 100644 index 0000000..c56dbfb --- /dev/null +++ b/lib/common/pylint_data/messages/bad-builtin/good.py @@ -0,0 +1,2 @@ +numbers = [2 * x for x in [1, 2, 3]] +print(numbers) diff --git a/lib/common/pylint_data/messages/bad-chained-comparison/bad.py b/lib/common/pylint_data/messages/bad-chained-comparison/bad.py new file mode 100644 index 0000000..c65e28a --- /dev/null +++ b/lib/common/pylint_data/messages/bad-chained-comparison/bad.py @@ -0,0 +1,14 @@ +shop = { + # animal: (specie, descriptions) + "parrot": ("Norvegian blue", ("restin'", "remarkable", "beautiful plumage")), +} + +if "parrot" in shop is "restin'": # [bad-chained-comparison] + print("Hellooooo, Pooolllllyyy ! WAAAAKEEY, WAKKEEEY !") + + +def xor_check(*, left=None, right=None): + if left is None != right is None: # [bad-chained-comparison] + raise ValueError( + "Either both left= and right= need to be provided or none should." + ) diff --git a/lib/common/pylint_data/messages/bad-chained-comparison/good.py b/lib/common/pylint_data/messages/bad-chained-comparison/good.py new file mode 100644 index 0000000..7a378c4 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-chained-comparison/good.py @@ -0,0 +1,14 @@ +shop = { + # animal: (specie, descriptions) + "parrot": ("Norvegian blue", ("restin'", "remarkable", "beautiful plumage")), +} + +if "parrot" in shop and "restin'" in shop["parrot"][1]: + print("Hellooooo, Pooolllllyyy ! WAAAAKEEY, WAKKEEEY !") + + +def xor_check(*, left=None, right=None): + if (left is None) != (right is None): + raise ValueError( + "Either both left= and right= need to be provided or none should." + ) diff --git a/lib/common/pylint_data/messages/bad-chained-comparison/related.md b/lib/common/pylint_data/messages/bad-chained-comparison/related.md new file mode 100644 index 0000000..98e2628 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-chained-comparison/related.md @@ -0,0 +1 @@ +- [Comparison Chaining](https://docs.python.org/3/reference/expressions.html#comparisons) diff --git a/lib/common/pylint_data/messages/bad-classmethod-argument/bad.py b/lib/common/pylint_data/messages/bad-classmethod-argument/bad.py new file mode 100644 index 0000000..b8c6d02 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-classmethod-argument/bad.py @@ -0,0 +1,4 @@ +class Klass: + @classmethod + def get_instance(self): # [bad-classmethod-argument] + return self() diff --git a/lib/common/pylint_data/messages/bad-classmethod-argument/good.py b/lib/common/pylint_data/messages/bad-classmethod-argument/good.py new file mode 100644 index 0000000..6097f1e --- /dev/null +++ b/lib/common/pylint_data/messages/bad-classmethod-argument/good.py @@ -0,0 +1,4 @@ +class Klass: + @classmethod + def get_instance(cls): + return cls() diff --git a/lib/common/pylint_data/messages/bad-configuration-section/details.md b/lib/common/pylint_data/messages/bad-configuration-section/details.md new file mode 100644 index 0000000..c82e948 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-configuration-section/details.md @@ -0,0 +1,3 @@ +This error was raised when we encountered an unexpected value type in a +toml configuration between pylint 2.12 and pylint 2.14 (before the +argparse refactor). diff --git a/lib/common/pylint_data/messages/bad-docstring-quotes/bad.py b/lib/common/pylint_data/messages/bad-docstring-quotes/bad.py new file mode 100644 index 0000000..bd800a4 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-docstring-quotes/bad.py @@ -0,0 +1,3 @@ +def foo(): # [bad-docstring-quotes] + "Docstring." + return diff --git a/lib/common/pylint_data/messages/bad-docstring-quotes/details.md b/lib/common/pylint_data/messages/bad-docstring-quotes/details.md new file mode 100644 index 0000000..4ce154a --- /dev/null +++ b/lib/common/pylint_data/messages/bad-docstring-quotes/details.md @@ -0,0 +1,2 @@ +From [PEP 257](https://peps.python.org/pep-0257/): + "For consistency, always use `"""triple double quotes"""` around docstrings." diff --git a/lib/common/pylint_data/messages/bad-docstring-quotes/good.py b/lib/common/pylint_data/messages/bad-docstring-quotes/good.py new file mode 100644 index 0000000..e5f6ceb --- /dev/null +++ b/lib/common/pylint_data/messages/bad-docstring-quotes/good.py @@ -0,0 +1,3 @@ +def foo(): + """Docstring.""" + return diff --git a/lib/common/pylint_data/messages/bad-docstring-quotes/related.md b/lib/common/pylint_data/messages/bad-docstring-quotes/related.md new file mode 100644 index 0000000..bd34abc --- /dev/null +++ b/lib/common/pylint_data/messages/bad-docstring-quotes/related.md @@ -0,0 +1 @@ +- [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/#specification) diff --git a/lib/common/pylint_data/messages/bad-dunder-name/bad.py b/lib/common/pylint_data/messages/bad-dunder-name/bad.py new file mode 100644 index 0000000..f01f650 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-dunder-name/bad.py @@ -0,0 +1,6 @@ +class Apples: + def _init_(self): # [bad-dunder-name] + pass + + def __hello__(self): # [bad-dunder-name] + print("hello") diff --git a/lib/common/pylint_data/messages/bad-dunder-name/good.py b/lib/common/pylint_data/messages/bad-dunder-name/good.py new file mode 100644 index 0000000..4f0adb9 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-dunder-name/good.py @@ -0,0 +1,6 @@ +class Apples: + def __init__(self): + pass + + def hello(self): + print("hello") diff --git a/lib/common/pylint_data/messages/bad-except-order/bad.py b/lib/common/pylint_data/messages/bad-except-order/bad.py new file mode 100644 index 0000000..482b515 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-except-order/bad.py @@ -0,0 +1,8 @@ +try: + print(int(input())) +except Exception: + raise +except TypeError: # [bad-except-order] + # This block cannot be reached since TypeError exception + # is caught by previous exception handler. + raise diff --git a/lib/common/pylint_data/messages/bad-except-order/good.py b/lib/common/pylint_data/messages/bad-except-order/good.py new file mode 100644 index 0000000..e9cd318 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-except-order/good.py @@ -0,0 +1,6 @@ +try: + print(int(input())) +except TypeError: + raise +except Exception: + raise diff --git a/lib/common/pylint_data/messages/bad-exception-cause/bad.py b/lib/common/pylint_data/messages/bad-exception-cause/bad.py new file mode 100644 index 0000000..ad4228a --- /dev/null +++ b/lib/common/pylint_data/messages/bad-exception-cause/bad.py @@ -0,0 +1,8 @@ +def divide(x, y): + result = 0 + try: + result = x / y + except ZeroDivisionError: + # +1: [bad-exception-cause] + raise ValueError(f"Division by zero when dividing {x} by {y} !") from result + return result diff --git a/lib/common/pylint_data/messages/bad-exception-cause/good.py b/lib/common/pylint_data/messages/bad-exception-cause/good.py new file mode 100644 index 0000000..3ccc47b --- /dev/null +++ b/lib/common/pylint_data/messages/bad-exception-cause/good.py @@ -0,0 +1,7 @@ +def divide(x, y): + result = 0 + try: + result = x / y + except ZeroDivisionError as exc: + raise ValueError(f"Division by zero when dividing {x} by {y} !") from exc + return result diff --git a/lib/common/pylint_data/messages/bad-exception-cause/related.md b/lib/common/pylint_data/messages/bad-exception-cause/related.md new file mode 100644 index 0000000..830b7f7 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-exception-cause/related.md @@ -0,0 +1,2 @@ +- [The raise statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) +- [Explicit Exception Chaining](https://peps.python.org/pep-3134/#explicit-exception-chaining) per [PEP 3134](https://peps.python.org/pep-3134/) diff --git a/lib/common/pylint_data/messages/bad-file-encoding/bad.py b/lib/common/pylint_data/messages/bad-file-encoding/bad.py new file mode 100644 index 0000000..a4ab46e --- /dev/null +++ b/lib/common/pylint_data/messages/bad-file-encoding/bad.py @@ -0,0 +1 @@ +# coding: latin_1 # [bad-file-encoding] diff --git a/lib/common/pylint_data/messages/bad-file-encoding/good.py b/lib/common/pylint_data/messages/bad-file-encoding/good.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/common/pylint_data/messages/bad-format-character/bad.py b/lib/common/pylint_data/messages/bad-format-character/bad.py new file mode 100644 index 0000000..3925849 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-format-character/bad.py @@ -0,0 +1 @@ +print("%s %z" % ("hello", "world")) # [bad-format-character] diff --git a/lib/common/pylint_data/messages/bad-format-character/details.md b/lib/common/pylint_data/messages/bad-format-character/details.md new file mode 100644 index 0000000..d287083 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-format-character/details.md @@ -0,0 +1,2 @@ +This check is currently only active for "old-style" string formatting as seen in the examples. +See [Issue #6085](https://github.com/pylint-dev/pylint/issues/6085) for more information. diff --git a/lib/common/pylint_data/messages/bad-format-character/good.py b/lib/common/pylint_data/messages/bad-format-character/good.py new file mode 100644 index 0000000..d8791a7 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-format-character/good.py @@ -0,0 +1 @@ +print("%s %s" % ("hello", "world")) diff --git a/lib/common/pylint_data/messages/bad-format-character/related.md b/lib/common/pylint_data/messages/bad-format-character/related.md new file mode 100644 index 0000000..210baa2 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-format-character/related.md @@ -0,0 +1,2 @@ +- [Format String Syntax](https://docs.python.org/3/library/string.html#formatstrings) +- [PyFormat](https://pyformat.info/) diff --git a/lib/common/pylint_data/messages/bad-format-string-key/bad.py b/lib/common/pylint_data/messages/bad-format-string-key/bad.py new file mode 100644 index 0000000..346d02d --- /dev/null +++ b/lib/common/pylint_data/messages/bad-format-string-key/bad.py @@ -0,0 +1 @@ +print("%(one)d" % {"one": 1, 2: 2}) # [bad-format-string-key] diff --git a/lib/common/pylint_data/messages/bad-format-string-key/details.md b/lib/common/pylint_data/messages/bad-format-string-key/details.md new file mode 100644 index 0000000..b88b191 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-format-string-key/details.md @@ -0,0 +1,6 @@ +This check only works for old-style string formatting using the **'%'** operator. + +This check only works if the dictionary with the values to be formatted is defined inline. +Passing a variable will not trigger the check as the other keys in this dictionary may be +used in other contexts, while an inline defined dictionary is clearly only intended to hold +the values that should be formatted. diff --git a/lib/common/pylint_data/messages/bad-format-string-key/good.py b/lib/common/pylint_data/messages/bad-format-string-key/good.py new file mode 100644 index 0000000..db7cfde --- /dev/null +++ b/lib/common/pylint_data/messages/bad-format-string-key/good.py @@ -0,0 +1 @@ +print("%(one)d, %(two)d" % {"one": 1, "two": 2}) diff --git a/lib/common/pylint_data/messages/bad-format-string/bad.py b/lib/common/pylint_data/messages/bad-format-string/bad.py new file mode 100644 index 0000000..4cb8112 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-format-string/bad.py @@ -0,0 +1 @@ +print("{a[0] + a[1]}".format(a=[0, 1])) # [bad-format-string] diff --git a/lib/common/pylint_data/messages/bad-format-string/good.py b/lib/common/pylint_data/messages/bad-format-string/good.py new file mode 100644 index 0000000..10475fd --- /dev/null +++ b/lib/common/pylint_data/messages/bad-format-string/good.py @@ -0,0 +1 @@ +print("{a[0]} + {a[1]}".format(a=[0, 1])) diff --git a/lib/common/pylint_data/messages/bad-format-string/related.md b/lib/common/pylint_data/messages/bad-format-string/related.md new file mode 100644 index 0000000..210baa2 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-format-string/related.md @@ -0,0 +1,2 @@ +- [Format String Syntax](https://docs.python.org/3/library/string.html#formatstrings) +- [PyFormat](https://pyformat.info/) diff --git a/lib/common/pylint_data/messages/bad-indentation/bad.py b/lib/common/pylint_data/messages/bad-indentation/bad.py new file mode 100644 index 0000000..dd5a8d9 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-indentation/bad.py @@ -0,0 +1,2 @@ +if input(): + print('yes') # [bad-indentation] diff --git a/lib/common/pylint_data/messages/bad-indentation/details.md b/lib/common/pylint_data/messages/bad-indentation/details.md new file mode 100644 index 0000000..8bb0fdf --- /dev/null +++ b/lib/common/pylint_data/messages/bad-indentation/details.md @@ -0,0 +1,2 @@ +The option `--indent-string` can be used to set the indentation unit for +this check. diff --git a/lib/common/pylint_data/messages/bad-indentation/good.py b/lib/common/pylint_data/messages/bad-indentation/good.py new file mode 100644 index 0000000..e7ba80a --- /dev/null +++ b/lib/common/pylint_data/messages/bad-indentation/good.py @@ -0,0 +1,2 @@ +if input(): + print("yes") diff --git a/lib/common/pylint_data/messages/bad-inline-option/bad.py b/lib/common/pylint_data/messages/bad-inline-option/bad.py new file mode 100644 index 0000000..b244da9 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-inline-option/bad.py @@ -0,0 +1,2 @@ +# 2:[bad-inline-option] +# pylint: disable line-too-long diff --git a/lib/common/pylint_data/messages/bad-inline-option/good.py b/lib/common/pylint_data/messages/bad-inline-option/good.py new file mode 100644 index 0000000..9799fff --- /dev/null +++ b/lib/common/pylint_data/messages/bad-inline-option/good.py @@ -0,0 +1 @@ +# pylint: disable=line-too-long diff --git a/lib/common/pylint_data/messages/bad-mcs-classmethod-argument/bad.py b/lib/common/pylint_data/messages/bad-mcs-classmethod-argument/bad.py new file mode 100644 index 0000000..1cc79be --- /dev/null +++ b/lib/common/pylint_data/messages/bad-mcs-classmethod-argument/bad.py @@ -0,0 +1,4 @@ +class Meta(type): + @classmethod + def foo(some): # [bad-mcs-classmethod-argument] + pass diff --git a/lib/common/pylint_data/messages/bad-mcs-classmethod-argument/good.py b/lib/common/pylint_data/messages/bad-mcs-classmethod-argument/good.py new file mode 100644 index 0000000..544fd54 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-mcs-classmethod-argument/good.py @@ -0,0 +1,4 @@ +class Meta(type): + @classmethod + def foo(mcs): + pass diff --git a/lib/common/pylint_data/messages/bad-mcs-method-argument/bad.py b/lib/common/pylint_data/messages/bad-mcs-method-argument/bad.py new file mode 100644 index 0000000..4bf47b2 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-mcs-method-argument/bad.py @@ -0,0 +1,3 @@ +class Meta(type): + def func(some): # [bad-mcs-method-argument] + pass diff --git a/lib/common/pylint_data/messages/bad-mcs-method-argument/good.py b/lib/common/pylint_data/messages/bad-mcs-method-argument/good.py new file mode 100644 index 0000000..2ba1252 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-mcs-method-argument/good.py @@ -0,0 +1,3 @@ +class Meta(type): + def func(cls): + pass diff --git a/lib/common/pylint_data/messages/bad-open-mode/bad.py b/lib/common/pylint_data/messages/bad-open-mode/bad.py new file mode 100644 index 0000000..59ad392 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-open-mode/bad.py @@ -0,0 +1,3 @@ +def open_and_get_content(file_path): + with open(file_path, "rwx") as file: # [bad-open-mode] + return file.read() diff --git a/lib/common/pylint_data/messages/bad-open-mode/good.py b/lib/common/pylint_data/messages/bad-open-mode/good.py new file mode 100644 index 0000000..b892c03 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-open-mode/good.py @@ -0,0 +1,3 @@ +def open_and_get_content(file_path): + with open(file_path, "r") as file: + return file.read() diff --git a/lib/common/pylint_data/messages/bad-plugin-value/details.md b/lib/common/pylint_data/messages/bad-plugin-value/details.md new file mode 100644 index 0000000..bdf0617 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-plugin-value/details.md @@ -0,0 +1,15 @@ +One of your pylint plugins cannot be loaded. There\'s nothing to change +in your code, but your pylint configuration or installation has an +issue. + +For example, there might be a typo. The following config: + + [MAIN] + load-plugins = pylint.extensions.bad_biultin + +Should be: + + [MAIN] + load-plugins = pylint.extensions.bad_builtin + +Or the plugin you added is not importable in your environment. diff --git a/lib/common/pylint_data/messages/bad-reversed-sequence/bad.py b/lib/common/pylint_data/messages/bad-reversed-sequence/bad.py new file mode 100644 index 0000000..19c5d1a --- /dev/null +++ b/lib/common/pylint_data/messages/bad-reversed-sequence/bad.py @@ -0,0 +1 @@ +reversed({1, 2, 3, 4}) # [bad-reversed-sequence] diff --git a/lib/common/pylint_data/messages/bad-reversed-sequence/good.py b/lib/common/pylint_data/messages/bad-reversed-sequence/good.py new file mode 100644 index 0000000..d396218 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-reversed-sequence/good.py @@ -0,0 +1 @@ +reversed([1, 2, 3, 4]) diff --git a/lib/common/pylint_data/messages/bad-staticmethod-argument/bad.py b/lib/common/pylint_data/messages/bad-staticmethod-argument/bad.py new file mode 100644 index 0000000..a46d65a --- /dev/null +++ b/lib/common/pylint_data/messages/bad-staticmethod-argument/bad.py @@ -0,0 +1,4 @@ +class Wolf: + @staticmethod + def eat(self): # [bad-staticmethod-argument] + pass diff --git a/lib/common/pylint_data/messages/bad-staticmethod-argument/good.py b/lib/common/pylint_data/messages/bad-staticmethod-argument/good.py new file mode 100644 index 0000000..7ad83f4 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-staticmethod-argument/good.py @@ -0,0 +1,4 @@ +class Wolf: + @staticmethod + def eat(sheep): + pass diff --git a/lib/common/pylint_data/messages/bad-str-strip-call/bad.py b/lib/common/pylint_data/messages/bad-str-strip-call/bad.py new file mode 100644 index 0000000..035dd8c --- /dev/null +++ b/lib/common/pylint_data/messages/bad-str-strip-call/bad.py @@ -0,0 +1,5 @@ +"Hello World".strip("Hello") # [bad-str-strip-call - the l is repeated] +# >>> ' World' + +"abcbc def bacabc".strip("abcbc ") # [bad-str-strip-call - b and c are repeated] +# >>> 'def' \ No newline at end of file diff --git a/lib/common/pylint_data/messages/bad-str-strip-call/details.md b/lib/common/pylint_data/messages/bad-str-strip-call/details.md new file mode 100644 index 0000000..02b326b --- /dev/null +++ b/lib/common/pylint_data/messages/bad-str-strip-call/details.md @@ -0,0 +1,11 @@ +A common misconception is that [str.strip(\'Hello\')]{.title-ref} +removes the *substring* [\'Hello\']{.title-ref} from the beginning and +end of the string. This is **not** the case. From the +\[documentation\](): + +\> The chars argument is not a prefix or suffix; rather, all +combinations of its values are stripped. + +Duplicated characters in the [str.strip]{.title-ref} call, besides not +having any effect on the actual result, may indicate this +misunderstanding, and lead to future bugs. diff --git a/lib/common/pylint_data/messages/bad-str-strip-call/good.py b/lib/common/pylint_data/messages/bad-str-strip-call/good.py new file mode 100644 index 0000000..1002c43 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-str-strip-call/good.py @@ -0,0 +1,5 @@ +"Hello World".strip("Helo") # [no repeated characters] +# >>> ' World' + +"abcbc def bacabc".strip("abc ") # [no repeated characters] +# >>> 'def' diff --git a/lib/common/pylint_data/messages/bad-str-strip-call/related.md b/lib/common/pylint_data/messages/bad-str-strip-call/related.md new file mode 100644 index 0000000..2f5e0a0 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-str-strip-call/related.md @@ -0,0 +1,2 @@ +- Documentation: + [str.strip(\[chars\])](https://docs.python.org/3/library/stdtypes.html?highlight=strip#str.strip) diff --git a/lib/common/pylint_data/messages/bad-string-format-type/bad.py b/lib/common/pylint_data/messages/bad-string-format-type/bad.py new file mode 100644 index 0000000..a58a5ba --- /dev/null +++ b/lib/common/pylint_data/messages/bad-string-format-type/bad.py @@ -0,0 +1 @@ +print("%d" % "1") # [bad-string-format-type] diff --git a/lib/common/pylint_data/messages/bad-string-format-type/details.md b/lib/common/pylint_data/messages/bad-string-format-type/details.md new file mode 100644 index 0000000..f218acf --- /dev/null +++ b/lib/common/pylint_data/messages/bad-string-format-type/details.md @@ -0,0 +1,3 @@ +This check is currently only active for \"old-style\" string formatting +as seen in the examples. +See [Issue #6085](https://github.com/pylint-dev/pylint/issues/6163) for more information. diff --git a/lib/common/pylint_data/messages/bad-string-format-type/good.py b/lib/common/pylint_data/messages/bad-string-format-type/good.py new file mode 100644 index 0000000..00ee8af --- /dev/null +++ b/lib/common/pylint_data/messages/bad-string-format-type/good.py @@ -0,0 +1 @@ +print("%d" % 1) diff --git a/lib/common/pylint_data/messages/bad-string-format-type/related.md b/lib/common/pylint_data/messages/bad-string-format-type/related.md new file mode 100644 index 0000000..210baa2 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-string-format-type/related.md @@ -0,0 +1,2 @@ +- [Format String Syntax](https://docs.python.org/3/library/string.html#formatstrings) +- [PyFormat](https://pyformat.info/) diff --git a/lib/common/pylint_data/messages/bad-super-call/bad.py b/lib/common/pylint_data/messages/bad-super-call/bad.py new file mode 100644 index 0000000..625a70e --- /dev/null +++ b/lib/common/pylint_data/messages/bad-super-call/bad.py @@ -0,0 +1,12 @@ +class Animal: + pass + + +class Tree: + pass + + +class Cat(Animal): + def __init__(self): + super(Tree, self).__init__() # [bad-super-call] + super(Animal, self).__init__() diff --git a/lib/common/pylint_data/messages/bad-super-call/details.md b/lib/common/pylint_data/messages/bad-super-call/details.md new file mode 100644 index 0000000..49da0bd --- /dev/null +++ b/lib/common/pylint_data/messages/bad-super-call/details.md @@ -0,0 +1,11 @@ +In Python 2.7, [super()]{.title-ref} has to be called with its own class +and [self]{.title-ref} as arguments ([super(Cat, self)]{.title-ref}), +which can lead to a mix up of parent and child class in the code. + +In Python 3 the recommended way is to call [super()]{.title-ref} +[without]{#without} [arguments]() (see also +[super-with-arguments]{.title-ref}). + +One exception is calling [super()]{.title-ref} on a non-direct parent +class. This can be used to get a method other than the default method +returned by the [mro()]{.title-ref}. diff --git a/lib/common/pylint_data/messages/bad-super-call/good.py b/lib/common/pylint_data/messages/bad-super-call/good.py new file mode 100644 index 0000000..66152bc --- /dev/null +++ b/lib/common/pylint_data/messages/bad-super-call/good.py @@ -0,0 +1,11 @@ +class Animal: + pass + + +class Tree: + pass + + +class Cat(Animal): + def __init__(self): + super(Animal, self).__init__() diff --git a/lib/common/pylint_data/messages/bad-super-call/related.md b/lib/common/pylint_data/messages/bad-super-call/related.md new file mode 100644 index 0000000..04f5a67 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-super-call/related.md @@ -0,0 +1,2 @@ +- \[Documentation for + super()\]() diff --git a/lib/common/pylint_data/messages/bad-thread-instantiation/bad.py b/lib/common/pylint_data/messages/bad-thread-instantiation/bad.py new file mode 100644 index 0000000..20fa72f --- /dev/null +++ b/lib/common/pylint_data/messages/bad-thread-instantiation/bad.py @@ -0,0 +1,9 @@ +import threading + + +def thread_target(n): + print(n**2) + + +thread = threading.Thread(lambda: None) # [bad-thread-instantiation] +thread.start() diff --git a/lib/common/pylint_data/messages/bad-thread-instantiation/good.py b/lib/common/pylint_data/messages/bad-thread-instantiation/good.py new file mode 100644 index 0000000..0dce7c3 --- /dev/null +++ b/lib/common/pylint_data/messages/bad-thread-instantiation/good.py @@ -0,0 +1,9 @@ +import threading + + +def thread_target(n): + print(n**2) + + +thread = threading.Thread(target=thread_target, args=(10,)) +thread.start() diff --git a/lib/common/pylint_data/messages/bare-except/bad.py b/lib/common/pylint_data/messages/bare-except/bad.py new file mode 100644 index 0000000..33dea31 --- /dev/null +++ b/lib/common/pylint_data/messages/bare-except/bad.py @@ -0,0 +1,4 @@ +try: + import platform_specific_module +except: # [bare-except] + platform_specific_module = None diff --git a/lib/common/pylint_data/messages/bare-except/details.md b/lib/common/pylint_data/messages/bare-except/details.md new file mode 100644 index 0000000..ef49eaa --- /dev/null +++ b/lib/common/pylint_data/messages/bare-except/details.md @@ -0,0 +1,6 @@ +A good rule of thumb is to limit use of bare 'except' clauses to two +cases: 1. If the exception handler will be printing out or logging the +traceback; at least the user will be aware that an error has occurred. +2. If the code needs to do some cleanup work, but then lets the +exception propagate upwards with raise. [try\...finally]{.title-ref} can +be a better way to handle this case. diff --git a/lib/common/pylint_data/messages/bare-except/good.py b/lib/common/pylint_data/messages/bare-except/good.py new file mode 100644 index 0000000..24baac9 --- /dev/null +++ b/lib/common/pylint_data/messages/bare-except/good.py @@ -0,0 +1,4 @@ +try: + import platform_specific_module +except ImportError: + platform_specific_module = None diff --git a/lib/common/pylint_data/messages/bare-except/related.md b/lib/common/pylint_data/messages/bare-except/related.md new file mode 100644 index 0000000..0ba8c97 --- /dev/null +++ b/lib/common/pylint_data/messages/bare-except/related.md @@ -0,0 +1,6 @@ +- \[Programming recommendation in + PEP8\]() +- \[PEP 760 -- No More Bare Excepts + (Rejected)\]() +- \[Discussion about PEP + 760\]() diff --git a/lib/common/pylint_data/messages/bare-name-capture-pattern/bad.py b/lib/common/pylint_data/messages/bare-name-capture-pattern/bad.py new file mode 100644 index 0000000..4fad74f --- /dev/null +++ b/lib/common/pylint_data/messages/bare-name-capture-pattern/bad.py @@ -0,0 +1,13 @@ +red = 0 +green = 1 +blue = 2 + + +def func(color): + match color: + case red: # [bare-name-capture-pattern] + print("I see red!") + case green: # [bare-name-capture-pattern] + print("Grass is green") + case blue: + print("I'm feeling the blues :(") diff --git a/lib/common/pylint_data/messages/bare-name-capture-pattern/good.py b/lib/common/pylint_data/messages/bare-name-capture-pattern/good.py new file mode 100644 index 0000000..70b18db --- /dev/null +++ b/lib/common/pylint_data/messages/bare-name-capture-pattern/good.py @@ -0,0 +1,17 @@ +from enum import Enum + + +class Color(Enum): + RED = 0 + GREEN = 1 + BLUE = 2 + + +def func(color: Color) -> None: + match color: + case Color.RED: + print("I see red!") + case Color.GREEN: + print("Grass is green") + case Color.BLUE: + print("I'm feeling the blues :(") diff --git a/lib/common/pylint_data/messages/bare-name-capture-pattern/related.md b/lib/common/pylint_data/messages/bare-name-capture-pattern/related.md new file mode 100644 index 0000000..ecedf7f --- /dev/null +++ b/lib/common/pylint_data/messages/bare-name-capture-pattern/related.md @@ -0,0 +1,2 @@ +- \[PEP + 636\]() diff --git a/lib/common/pylint_data/messages/bidirectional-unicode/bad.py b/lib/common/pylint_data/messages/bidirectional-unicode/bad.py new file mode 100644 index 0000000..52f3070 --- /dev/null +++ b/lib/common/pylint_data/messages/bidirectional-unicode/bad.py @@ -0,0 +1,2 @@ +# +1: [bidirectional-unicode] +example = "x‏" * 100 # "‏x" is assigned diff --git a/lib/common/pylint_data/messages/bidirectional-unicode/good.py b/lib/common/pylint_data/messages/bidirectional-unicode/good.py new file mode 100644 index 0000000..4a625dd --- /dev/null +++ b/lib/common/pylint_data/messages/bidirectional-unicode/good.py @@ -0,0 +1 @@ +example = "x[U+2194]" * 100 diff --git a/lib/common/pylint_data/messages/binary-op-exception/bad.py b/lib/common/pylint_data/messages/binary-op-exception/bad.py new file mode 100644 index 0000000..8ffd1de --- /dev/null +++ b/lib/common/pylint_data/messages/binary-op-exception/bad.py @@ -0,0 +1,4 @@ +try: + 1 / 0 +except ZeroDivisionError or ValueError: # [binary-op-exception] + pass diff --git a/lib/common/pylint_data/messages/binary-op-exception/good.py b/lib/common/pylint_data/messages/binary-op-exception/good.py new file mode 100644 index 0000000..945e422 --- /dev/null +++ b/lib/common/pylint_data/messages/binary-op-exception/good.py @@ -0,0 +1,4 @@ +try: + 1 / 0 +except (ZeroDivisionError, ValueError): + pass diff --git a/lib/common/pylint_data/messages/boolean-datetime/bad.py b/lib/common/pylint_data/messages/boolean-datetime/bad.py new file mode 100644 index 0000000..c36abd1 --- /dev/null +++ b/lib/common/pylint_data/messages/boolean-datetime/bad.py @@ -0,0 +1,8 @@ +import datetime + +if datetime.time(): # [boolean-datetime] + print("It is time.") + + +if datetime.datetime.now().time(): # [boolean-datetime] + print("Now or never.") diff --git a/lib/common/pylint_data/messages/boolean-datetime/good.py b/lib/common/pylint_data/messages/boolean-datetime/good.py new file mode 100644 index 0000000..b846e0b --- /dev/null +++ b/lib/common/pylint_data/messages/boolean-datetime/good.py @@ -0,0 +1,9 @@ +import datetime + +time_now_utc = datetime.datetime.now(tz=datetime.UTC).time() + +if time_now_utc > datetime.time(6, 0): + print("Daytime!") + +if time_now_utc < datetime.time(6, 0): + print("Nighttime!") diff --git a/lib/common/pylint_data/messages/boolean-datetime/related.md b/lib/common/pylint_data/messages/boolean-datetime/related.md new file mode 100644 index 0000000..7478ff3 --- /dev/null +++ b/lib/common/pylint_data/messages/boolean-datetime/related.md @@ -0,0 +1 @@ +- \[Python bug tracker\]() diff --git a/lib/common/pylint_data/messages/break-in-finally/bad.py b/lib/common/pylint_data/messages/break-in-finally/bad.py new file mode 100644 index 0000000..6d36006 --- /dev/null +++ b/lib/common/pylint_data/messages/break-in-finally/bad.py @@ -0,0 +1,5 @@ +while True: + try: + pass + finally: + break # [break-in-finally] diff --git a/lib/common/pylint_data/messages/break-in-finally/good.py b/lib/common/pylint_data/messages/break-in-finally/good.py new file mode 100644 index 0000000..87189de --- /dev/null +++ b/lib/common/pylint_data/messages/break-in-finally/good.py @@ -0,0 +1,7 @@ +while True: + try: + pass + except ValueError: + pass + else: + break diff --git a/lib/common/pylint_data/messages/break-in-finally/related.md b/lib/common/pylint_data/messages/break-in-finally/related.md new file mode 100644 index 0000000..be29d1b --- /dev/null +++ b/lib/common/pylint_data/messages/break-in-finally/related.md @@ -0,0 +1,4 @@ +- \[Python 3 docs \'finally\' + clause\]() +- \[PEP 765 - Disallow return/break/continue that exit a finally + block\]() diff --git a/lib/common/pylint_data/messages/broad-exception-caught/bad.py b/lib/common/pylint_data/messages/broad-exception-caught/bad.py new file mode 100644 index 0000000..67d5d7b --- /dev/null +++ b/lib/common/pylint_data/messages/broad-exception-caught/bad.py @@ -0,0 +1,4 @@ +try: + import platform_specific_module +except Exception: # [broad-exception-caught] + platform_specific_module = None diff --git a/lib/common/pylint_data/messages/broad-exception-caught/details.md b/lib/common/pylint_data/messages/broad-exception-caught/details.md new file mode 100644 index 0000000..09fc708 --- /dev/null +++ b/lib/common/pylint_data/messages/broad-exception-caught/details.md @@ -0,0 +1,4 @@ +For example, you\'re trying to import a library with required system +dependencies and you catch everything instead of only import errors, you +will miss the error message telling you, that your code could work if +you had installed the system dependencies. diff --git a/lib/common/pylint_data/messages/broad-exception-caught/good.py b/lib/common/pylint_data/messages/broad-exception-caught/good.py new file mode 100644 index 0000000..24baac9 --- /dev/null +++ b/lib/common/pylint_data/messages/broad-exception-caught/good.py @@ -0,0 +1,4 @@ +try: + import platform_specific_module +except ImportError: + platform_specific_module = None diff --git a/lib/common/pylint_data/messages/broad-exception-caught/related.md b/lib/common/pylint_data/messages/broad-exception-caught/related.md new file mode 100644 index 0000000..ea76996 --- /dev/null +++ b/lib/common/pylint_data/messages/broad-exception-caught/related.md @@ -0,0 +1,2 @@ +- [Should I always specify an exception type in \'except\' + statements?](https://stackoverflow.com/a/14797508/2519059) diff --git a/lib/common/pylint_data/messages/broad-exception-raised/bad.py b/lib/common/pylint_data/messages/broad-exception-raised/bad.py new file mode 100644 index 0000000..4c8ff3b --- /dev/null +++ b/lib/common/pylint_data/messages/broad-exception-raised/bad.py @@ -0,0 +1,4 @@ +def small_apple(apple, length): + if len(apple) < length: + raise Exception("Apple is too small!") # [broad-exception-raised] + print(f"{apple} is proper size.") diff --git a/lib/common/pylint_data/messages/broad-exception-raised/good.py b/lib/common/pylint_data/messages/broad-exception-raised/good.py new file mode 100644 index 0000000..a63b1b3 --- /dev/null +++ b/lib/common/pylint_data/messages/broad-exception-raised/good.py @@ -0,0 +1,4 @@ +def small_apple(apple, length): + if len(apple) < length: + raise ValueError("Apple is too small!") + print(f"{apple} is proper size.") diff --git a/lib/common/pylint_data/messages/broad-exception-raised/related.md b/lib/common/pylint_data/messages/broad-exception-raised/related.md new file mode 100644 index 0000000..d013cab --- /dev/null +++ b/lib/common/pylint_data/messages/broad-exception-raised/related.md @@ -0,0 +1,2 @@ +- [Programming recommendation in + PEP8](https://peps.python.org/pep-0008/#programming-recommendations) diff --git a/lib/common/pylint_data/messages/broken-collections-callable/bad.py b/lib/common/pylint_data/messages/broken-collections-callable/bad.py new file mode 100644 index 0000000..a90a992 --- /dev/null +++ b/lib/common/pylint_data/messages/broken-collections-callable/bad.py @@ -0,0 +1,6 @@ +from collections.abc import Callable +from typing import Optional + + +def func() -> Optional[Callable[[int], None]]: # [broken-collections-callable] + ... diff --git a/lib/common/pylint_data/messages/broken-collections-callable/good.py b/lib/common/pylint_data/messages/broken-collections-callable/good.py new file mode 100644 index 0000000..c89d593 --- /dev/null +++ b/lib/common/pylint_data/messages/broken-collections-callable/good.py @@ -0,0 +1,4 @@ +from typing import Callable, Optional + + +def func() -> Optional[Callable[[int], None]]: ... diff --git a/lib/common/pylint_data/messages/broken-collections-callable/related.md b/lib/common/pylint_data/messages/broken-collections-callable/related.md new file mode 100644 index 0000000..b1669e0 --- /dev/null +++ b/lib/common/pylint_data/messages/broken-collections-callable/related.md @@ -0,0 +1 @@ +- [bpo-42965](https://bugs.python.org/issue42965) diff --git a/lib/common/pylint_data/messages/broken-noreturn/bad.py b/lib/common/pylint_data/messages/broken-noreturn/bad.py new file mode 100644 index 0000000..77baf76 --- /dev/null +++ b/lib/common/pylint_data/messages/broken-noreturn/bad.py @@ -0,0 +1,5 @@ +from typing import NoReturn, Union + + +def exploding_apple(apple) -> Union[None, NoReturn]: # [broken-noreturn] + print(f"{apple} is about to explode") diff --git a/lib/common/pylint_data/messages/broken-noreturn/good.py b/lib/common/pylint_data/messages/broken-noreturn/good.py new file mode 100644 index 0000000..ce4dc6e --- /dev/null +++ b/lib/common/pylint_data/messages/broken-noreturn/good.py @@ -0,0 +1,6 @@ +from typing import NoReturn + + +def exploding_apple(apple) -> NoReturn: + print(f"{apple} is about to explode") + raise Exception("{apple} exploded !") diff --git a/lib/common/pylint_data/messages/c-extension-no-member/details.md b/lib/common/pylint_data/messages/c-extension-no-member/details.md new file mode 100644 index 0000000..00d58d3 --- /dev/null +++ b/lib/common/pylint_data/messages/c-extension-no-member/details.md @@ -0,0 +1,4 @@ +`c-extension-no-member` is an informational variant of `no-member` to +encourage allowing introspection of C extensions as described in the +[page](https://pylint.readthedocs.io/en/latest/user_guide/messages/error/no-member.html) +for `no-member`. diff --git a/lib/common/pylint_data/messages/c-extension-no-member/good.py b/lib/common/pylint_data/messages/c-extension-no-member/good.py new file mode 100644 index 0000000..c40beb5 --- /dev/null +++ b/lib/common/pylint_data/messages/c-extension-no-member/good.py @@ -0,0 +1 @@ +# This is a placeholder for correct code for this message. diff --git a/lib/common/pylint_data/messages/catching-non-exception/bad.py b/lib/common/pylint_data/messages/catching-non-exception/bad.py new file mode 100644 index 0000000..86fa07b --- /dev/null +++ b/lib/common/pylint_data/messages/catching-non-exception/bad.py @@ -0,0 +1,8 @@ +class FooError: + pass + + +try: + 1 / 0 +except FooError: # [catching-non-exception] + pass diff --git a/lib/common/pylint_data/messages/catching-non-exception/good.py b/lib/common/pylint_data/messages/catching-non-exception/good.py new file mode 100644 index 0000000..342fada --- /dev/null +++ b/lib/common/pylint_data/messages/catching-non-exception/good.py @@ -0,0 +1,8 @@ +class FooError(Exception): + pass + + +try: + 1 / 0 +except FooError: + pass diff --git a/lib/common/pylint_data/messages/cell-var-from-loop/bad.py b/lib/common/pylint_data/messages/cell-var-from-loop/bad.py new file mode 100644 index 0000000..c8b9e92 --- /dev/null +++ b/lib/common/pylint_data/messages/cell-var-from-loop/bad.py @@ -0,0 +1,23 @@ +def teacher_greeting(names): + greetings = [] + for name in names: + + def greet(): + # do something + print(f"Hello, {name}!") # [cell-var-from-loop] + + if name.isalpha(): + greetings.append(greet) + + for greet in greetings: + # the "name" variable is evaluated when the function is called here, + # which is the last value it had in the loop - "Not-A-Name" + greet() + + +teacher_greeting(["Graham", "John", "Terry", "Eric", "Terry", "Michael"]) +# "Hello, Michael!" +# "Hello, Michael!" +# "Hello, Michael!" +# "Hello, Michael!" +# "Hello, Michael!" diff --git a/lib/common/pylint_data/messages/cell-var-from-loop/good.py b/lib/common/pylint_data/messages/cell-var-from-loop/good.py new file mode 100644 index 0000000..9f07c00 --- /dev/null +++ b/lib/common/pylint_data/messages/cell-var-from-loop/good.py @@ -0,0 +1,33 @@ +def teacher_greeting(names): + def greet(name): + # do something + print(f"Hello, {name}!") + + for name in names: + if name.isalpha(): + # we're passing the value of "name" to the function here + greet(name) + + +teacher_greeting(["Graham", "John", "Terry", "Eric", "Terry", "Michael"]) + +### + +import functools + + +def teacher_greeting(names): + greetings = [] + for name in names: + if name.isalpha(): + # "name" is evaluated when the partial is created here, so this + # does not do lazy evaluation + greetings.append(functools.partial(print, f"Hello, {name}!")) + + for greet in greetings: + # `partial`s are called like functions, but you've already passed the + # arguments to them + greet() + + +teacher_greeting(["Graham", "John", "Terry", "Eric", "Terry", "Michael"]) diff --git a/lib/common/pylint_data/messages/cell-var-from-loop/related.md b/lib/common/pylint_data/messages/cell-var-from-loop/related.md new file mode 100644 index 0000000..97d6b2e --- /dev/null +++ b/lib/common/pylint_data/messages/cell-var-from-loop/related.md @@ -0,0 +1,2 @@ +- [Stackoverflow + discussion](https://stackoverflow.com/questions/25314547/cell-var-from-loop-warning-from-pylint) diff --git a/lib/common/pylint_data/messages/chained-comparison/bad.py b/lib/common/pylint_data/messages/chained-comparison/bad.py new file mode 100644 index 0000000..b04c927 --- /dev/null +++ b/lib/common/pylint_data/messages/chained-comparison/bad.py @@ -0,0 +1,5 @@ +a = int(input()) +b = int(input()) +c = int(input()) +if a < b and b < c: # [chained-comparison] + pass diff --git a/lib/common/pylint_data/messages/chained-comparison/good.py b/lib/common/pylint_data/messages/chained-comparison/good.py new file mode 100644 index 0000000..9c4abff --- /dev/null +++ b/lib/common/pylint_data/messages/chained-comparison/good.py @@ -0,0 +1,5 @@ +a = int(input()) +b = int(input()) +c = int(input()) +if a < b < c: + pass diff --git a/lib/common/pylint_data/messages/class-variable-slots-conflict/bad.py b/lib/common/pylint_data/messages/class-variable-slots-conflict/bad.py new file mode 100644 index 0000000..7705669 --- /dev/null +++ b/lib/common/pylint_data/messages/class-variable-slots-conflict/bad.py @@ -0,0 +1,15 @@ +class Person: + # +1: [class-variable-slots-conflict, class-variable-slots-conflict, class-variable-slots-conflict] + __slots__ = ("age", "name", "say_hi") + name = None + + def __init__(self, age, name): + self.age = age + self.name = name + + @property + def age(self): + return self.age + + def say_hi(self): + print(f"Hi, I'm {self.name}.") diff --git a/lib/common/pylint_data/messages/class-variable-slots-conflict/good.py b/lib/common/pylint_data/messages/class-variable-slots-conflict/good.py new file mode 100644 index 0000000..2428f78 --- /dev/null +++ b/lib/common/pylint_data/messages/class-variable-slots-conflict/good.py @@ -0,0 +1,13 @@ +class Person: + __slots__ = ("_age", "name") + + def __init__(self, age, name): + self._age = age + self.name = name + + @property + def age(self): + return self._age + + def say_hi(self): + print(f"Hi, I'm {self.name}.") diff --git a/lib/common/pylint_data/messages/comparison-of-constants/bad.py b/lib/common/pylint_data/messages/comparison-of-constants/bad.py new file mode 100644 index 0000000..2ea0c42 --- /dev/null +++ b/lib/common/pylint_data/messages/comparison-of-constants/bad.py @@ -0,0 +1,2 @@ +def is_the_answer() -> bool: + return 42 == 42 # [comparison-of-constants] diff --git a/lib/common/pylint_data/messages/comparison-of-constants/good.py b/lib/common/pylint_data/messages/comparison-of-constants/good.py new file mode 100644 index 0000000..731bb16 --- /dev/null +++ b/lib/common/pylint_data/messages/comparison-of-constants/good.py @@ -0,0 +1,2 @@ +def is_the_answer(meaning_of_life: int) -> bool: + return meaning_of_life == 42 diff --git a/lib/common/pylint_data/messages/comparison-with-callable/bad.py b/lib/common/pylint_data/messages/comparison-with-callable/bad.py new file mode 100644 index 0000000..1dbdad0 --- /dev/null +++ b/lib/common/pylint_data/messages/comparison-with-callable/bad.py @@ -0,0 +1,7 @@ +def function_returning_a_fruit() -> str: + return "orange" + + +def is_an_orange(fruit: str = "apple"): + # apple == + return fruit == function_returning_a_fruit # [comparison-with-callable] diff --git a/lib/common/pylint_data/messages/comparison-with-callable/good.py b/lib/common/pylint_data/messages/comparison-with-callable/good.py new file mode 100644 index 0000000..a490071 --- /dev/null +++ b/lib/common/pylint_data/messages/comparison-with-callable/good.py @@ -0,0 +1,7 @@ +def function_returning_a_fruit() -> str: + return "orange" + + +def is_an_orange(fruit: str = "apple"): + # apple == orange + return fruit == function_returning_a_fruit() diff --git a/lib/common/pylint_data/messages/comparison-with-itself/bad.py b/lib/common/pylint_data/messages/comparison-with-itself/bad.py new file mode 100644 index 0000000..77794bc --- /dev/null +++ b/lib/common/pylint_data/messages/comparison-with-itself/bad.py @@ -0,0 +1,3 @@ +def is_an_orange(fruit): + an_orange = "orange" + return fruit == fruit # [comparison-with-itself] diff --git a/lib/common/pylint_data/messages/comparison-with-itself/good.py b/lib/common/pylint_data/messages/comparison-with-itself/good.py new file mode 100644 index 0000000..b2b4296 --- /dev/null +++ b/lib/common/pylint_data/messages/comparison-with-itself/good.py @@ -0,0 +1,3 @@ +def is_an_orange(fruit): + an_orange = "orange" + return an_orange == fruit diff --git a/lib/common/pylint_data/messages/condition-evals-to-constant/bad.py b/lib/common/pylint_data/messages/condition-evals-to-constant/bad.py new file mode 100644 index 0000000..f52b24f --- /dev/null +++ b/lib/common/pylint_data/messages/condition-evals-to-constant/bad.py @@ -0,0 +1,2 @@ +def is_a_fruit(fruit): + return bool(fruit in {"apple", "orange"} or True) # [condition-evals-to-constant] diff --git a/lib/common/pylint_data/messages/condition-evals-to-constant/good.py b/lib/common/pylint_data/messages/condition-evals-to-constant/good.py new file mode 100644 index 0000000..37e9754 --- /dev/null +++ b/lib/common/pylint_data/messages/condition-evals-to-constant/good.py @@ -0,0 +1,2 @@ +def is_a_fruit(fruit): + return fruit in {"apple", "orange"} diff --git a/lib/common/pylint_data/messages/config-parse-error/details.md b/lib/common/pylint_data/messages/config-parse-error/details.md new file mode 100644 index 0000000..629ed6f --- /dev/null +++ b/lib/common/pylint_data/messages/config-parse-error/details.md @@ -0,0 +1,2 @@ +This is a message linked to a problem in your configuration not your +code. diff --git a/lib/common/pylint_data/messages/confusing-consecutive-elif/bad.py b/lib/common/pylint_data/messages/confusing-consecutive-elif/bad.py new file mode 100644 index 0000000..93e1e2d --- /dev/null +++ b/lib/common/pylint_data/messages/confusing-consecutive-elif/bad.py @@ -0,0 +1,6 @@ +def myfunc(shall_continue: bool, shall_exit: bool): + if shall_continue: + if input("Are you sure?") == "y": + print("Moving on.") + elif shall_exit: # [confusing-consecutive-elif] + print("Exiting.") diff --git a/lib/common/pylint_data/messages/confusing-consecutive-elif/details.md b/lib/common/pylint_data/messages/confusing-consecutive-elif/details.md new file mode 100644 index 0000000..2a0689b --- /dev/null +++ b/lib/common/pylint_data/messages/confusing-consecutive-elif/details.md @@ -0,0 +1,3 @@ +Creating a function for the nested conditional, or adding an explicit +`else` in the indented `if` statement, even if it only contains a `pass` +statement, can help clarify the code. diff --git a/lib/common/pylint_data/messages/confusing-consecutive-elif/good.py b/lib/common/pylint_data/messages/confusing-consecutive-elif/good.py new file mode 100644 index 0000000..1722a6b --- /dev/null +++ b/lib/common/pylint_data/messages/confusing-consecutive-elif/good.py @@ -0,0 +1,22 @@ +# Option 1: add explicit 'else' +def myfunc(shall_continue: bool, shall_exit: bool): + if shall_continue: + if input("Are you sure?") == "y": + print("Moving on.") + else: + pass + elif shall_exit: + print("Exiting.") + + +# Option 2: extract function +def user_confirmation(): + if input("Are you sure?") == "y": + print("Moving on.") + + +def myfunc2(shall_continue: bool, shall_exit: bool): + if shall_continue: + user_confirmation() + elif shall_exit: + print("Exiting.") diff --git a/lib/common/pylint_data/messages/confusing-with-statement/bad.py b/lib/common/pylint_data/messages/confusing-with-statement/bad.py new file mode 100644 index 0000000..e6570a5 --- /dev/null +++ b/lib/common/pylint_data/messages/confusing-with-statement/bad.py @@ -0,0 +1,2 @@ +with open("file.txt", "w") as fh1, fh2: # [confusing-with-statement] + pass diff --git a/lib/common/pylint_data/messages/confusing-with-statement/good.py b/lib/common/pylint_data/messages/confusing-with-statement/good.py new file mode 100644 index 0000000..bcedaaf --- /dev/null +++ b/lib/common/pylint_data/messages/confusing-with-statement/good.py @@ -0,0 +1,3 @@ +with open("file.txt", "w", encoding="utf8") as fh1: + with open("file.txt", "w", encoding="utf8") as fh2: + pass diff --git a/lib/common/pylint_data/messages/consider-alternative-union-syntax/bad.py b/lib/common/pylint_data/messages/consider-alternative-union-syntax/bad.py new file mode 100644 index 0000000..2f83628 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-alternative-union-syntax/bad.py @@ -0,0 +1,8 @@ +from typing import Optional, Union + + +def forecast( + temp: Union[int, float], # [consider-alternative-union-syntax] + unit: Optional[str], # [consider-alternative-union-syntax] +) -> None: + print(f'Temperature: {temp}{unit or ""}') diff --git a/lib/common/pylint_data/messages/consider-alternative-union-syntax/details.md b/lib/common/pylint_data/messages/consider-alternative-union-syntax/details.md new file mode 100644 index 0000000..161d097 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-alternative-union-syntax/details.md @@ -0,0 +1,5 @@ +Using the shorthand syntax for union types is [recommended over the +`typing` module](https://docs.python.org/3/library/typing.html#typing.Union). + This is consistent with the broader recommendation to prefer built-in types over imports (for example, using `list` instead of the now-deprecated `typing.List`). + +`typing.Optional` can also cause confusion in annotated function arguments, since an argument annotated as `Optional` is still a *required* argument when a default value is not set. Explicitly annotating such arguments with `type | None` makes the intention clear. diff --git a/lib/common/pylint_data/messages/consider-alternative-union-syntax/good.py b/lib/common/pylint_data/messages/consider-alternative-union-syntax/good.py new file mode 100644 index 0000000..29f8797 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-alternative-union-syntax/good.py @@ -0,0 +1,2 @@ +def forecast(temp: int | float, unit: str | None) -> None: + print(f'Temperature: {temp}{unit or ""}') diff --git a/lib/common/pylint_data/messages/consider-iterating-dictionary/bad.py b/lib/common/pylint_data/messages/consider-iterating-dictionary/bad.py new file mode 100644 index 0000000..eb5a97a --- /dev/null +++ b/lib/common/pylint_data/messages/consider-iterating-dictionary/bad.py @@ -0,0 +1,5 @@ +FRUITS = {"apple": 1, "pear": 5, "peach": 10} + + +for fruit in FRUITS.keys(): # [consider-iterating-dictionary] + print(fruit) diff --git a/lib/common/pylint_data/messages/consider-iterating-dictionary/good.py b/lib/common/pylint_data/messages/consider-iterating-dictionary/good.py new file mode 100644 index 0000000..c67b399 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-iterating-dictionary/good.py @@ -0,0 +1,5 @@ +FRUITS = {"apple": 1, "pear": 5, "peach": 10} + + +for fruit in FRUITS: + print(fruit) diff --git a/lib/common/pylint_data/messages/consider-math-not-float/bad.py b/lib/common/pylint_data/messages/consider-math-not-float/bad.py new file mode 100644 index 0000000..6cab739 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-math-not-float/bad.py @@ -0,0 +1 @@ +swag = float("inf") # [consider-math-not-float] diff --git a/lib/common/pylint_data/messages/consider-math-not-float/details.md b/lib/common/pylint_data/messages/consider-math-not-float/details.md new file mode 100644 index 0000000..a79e286 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-math-not-float/details.md @@ -0,0 +1,43 @@ +This is an extension check because the typing advantage could be fixed. + +Regarding performance, float(\"nan\") and float(\"inf\") are slower than +their counterpart math.inf and math.nan by a factor of 4 after the +initial import of math. + +``` python +import math +import timeit + +time_math_inf = timeit.timeit('math.nan', globals=globals(), number=10**8) +print(f'math.nan: {time_math_inf:.2f} seconds') + +import timeit +time_inf_str = timeit.timeit('float("nan")', number=10**8) +print(f'float("nan"): {time_inf_str:.2f} seconds') +``` + +Result: + + math.nan: 1.24 seconds + float("nan"): 5.15 seconds + +But if we take the initial import into account it\'s worse. + +``` python +import timeit + +time_math_inf = timeit.timeit('import math;math.nan', globals=globals(), number=10**8) +print(f'math.nan: {time_math_inf:.2f} seconds') + +import timeit +time_inf_str = timeit.timeit('float("nan")', number=10**8) +print(f'float("nan"): {time_inf_str:.2f} seconds') +``` + +Result: + + math.nan: 9.08 seconds + float("nan"): 5.33 seconds + +So the decision depends on how and how often you need to use it and what +matter to you. diff --git a/lib/common/pylint_data/messages/consider-math-not-float/good.py b/lib/common/pylint_data/messages/consider-math-not-float/good.py new file mode 100644 index 0000000..711a0f3 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-math-not-float/good.py @@ -0,0 +1,3 @@ +import math + +swag = math.inf diff --git a/lib/common/pylint_data/messages/consider-merging-isinstance/bad.py b/lib/common/pylint_data/messages/consider-merging-isinstance/bad.py new file mode 100644 index 0000000..c7b059c --- /dev/null +++ b/lib/common/pylint_data/messages/consider-merging-isinstance/bad.py @@ -0,0 +1,6 @@ +from typing import Any + + +def is_number(value: Any) -> bool: + # +1: [consider-merging-isinstance] + return isinstance(value, int) or isinstance(value, float) diff --git a/lib/common/pylint_data/messages/consider-merging-isinstance/good.py b/lib/common/pylint_data/messages/consider-merging-isinstance/good.py new file mode 100644 index 0000000..993f690 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-merging-isinstance/good.py @@ -0,0 +1,5 @@ +from typing import Any + + +def is_number(value: Any) -> bool: + return isinstance(value, (int, float)) diff --git a/lib/common/pylint_data/messages/consider-refactoring-into-while-condition/bad.py b/lib/common/pylint_data/messages/consider-refactoring-into-while-condition/bad.py new file mode 100644 index 0000000..edb6fe3 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-refactoring-into-while-condition/bad.py @@ -0,0 +1,7 @@ +fruit_basket = ["apple", "orange", "banana", "cherry", "guava"] + +while True: # [consider-refactoring-into-while-condition] + if len(fruit_basket) == 0: + break + fruit = fruit_basket.pop() + print(f"We removed {fruit} from the basket") diff --git a/lib/common/pylint_data/messages/consider-refactoring-into-while-condition/good.py b/lib/common/pylint_data/messages/consider-refactoring-into-while-condition/good.py new file mode 100644 index 0000000..900b9c6 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-refactoring-into-while-condition/good.py @@ -0,0 +1,5 @@ +fruit_basket = ["apple", "orange", "banana", "cherry", "guava"] + +while len(fruit_basket) != 0: + fruit = fruit_basket.pop() + print(f"We removed {fruit} from the basket") diff --git a/lib/common/pylint_data/messages/consider-swap-variables/bad.py b/lib/common/pylint_data/messages/consider-swap-variables/bad.py new file mode 100644 index 0000000..2092c99 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-swap-variables/bad.py @@ -0,0 +1,6 @@ +a = 1 +b = 2 + +temp = a # [consider-swap-variables] +a = b +b = temp diff --git a/lib/common/pylint_data/messages/consider-swap-variables/good.py b/lib/common/pylint_data/messages/consider-swap-variables/good.py new file mode 100644 index 0000000..1b92dcb --- /dev/null +++ b/lib/common/pylint_data/messages/consider-swap-variables/good.py @@ -0,0 +1,4 @@ +a = 1 +b = 2 + +a, b = b, a diff --git a/lib/common/pylint_data/messages/consider-ternary-expression/bad.py b/lib/common/pylint_data/messages/consider-ternary-expression/bad.py new file mode 100644 index 0000000..126b92b --- /dev/null +++ b/lib/common/pylint_data/messages/consider-ternary-expression/bad.py @@ -0,0 +1,5 @@ +x, y = input(), input() +if x >= y: # [consider-ternary-expression] + maximum = x +else: + maximum = y diff --git a/lib/common/pylint_data/messages/consider-ternary-expression/good.py b/lib/common/pylint_data/messages/consider-ternary-expression/good.py new file mode 100644 index 0000000..1173240 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-ternary-expression/good.py @@ -0,0 +1,2 @@ +x, y = input(), input() +maximum = x if x >= y else y diff --git a/lib/common/pylint_data/messages/consider-using-alias/bad.py b/lib/common/pylint_data/messages/consider-using-alias/bad.py new file mode 100644 index 0000000..e3d0785 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-alias/bad.py @@ -0,0 +1,3 @@ +import typing + +cats: typing.Dict[str, int] # [consider-using-alias] diff --git a/lib/common/pylint_data/messages/consider-using-alias/good.py b/lib/common/pylint_data/messages/consider-using-alias/good.py new file mode 100644 index 0000000..4277488 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-alias/good.py @@ -0,0 +1,3 @@ +import typing + +cats: typing.cast(dict[str, int], "string") diff --git a/lib/common/pylint_data/messages/consider-using-any-or-all/bad.py b/lib/common/pylint_data/messages/consider-using-any-or-all/bad.py new file mode 100644 index 0000000..cba32c6 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-any-or-all/bad.py @@ -0,0 +1,14 @@ +def all_even(items): + """Return True if the list contains all even numbers""" + for item in items: # [consider-using-any-or-all] + if not item % 2 == 0: + return False + return True + + +def any_even(items): + """Return True if the list contains any even numbers""" + for item in items: # [consider-using-any-or-all] + if item % 2 == 0: + return True + return False diff --git a/lib/common/pylint_data/messages/consider-using-any-or-all/good.py b/lib/common/pylint_data/messages/consider-using-any-or-all/good.py new file mode 100644 index 0000000..af8efaa --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-any-or-all/good.py @@ -0,0 +1,8 @@ +def all_even(items): + """Return True if the list contains all even numbers""" + return all(item % 2 == 0 for item in items) + + +def any_even(items): + """Return True if the list contains any even numbers""" + return any(item % 2 == 0 for item in items) diff --git a/lib/common/pylint_data/messages/consider-using-assignment-expr/bad.py b/lib/common/pylint_data/messages/consider-using-assignment-expr/bad.py new file mode 100644 index 0000000..a700537 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-assignment-expr/bad.py @@ -0,0 +1,4 @@ +apples = 2 + +if apples: # [consider-using-assignment-expr] + print("God apples!") diff --git a/lib/common/pylint_data/messages/consider-using-assignment-expr/good.py b/lib/common/pylint_data/messages/consider-using-assignment-expr/good.py new file mode 100644 index 0000000..a1e4027 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-assignment-expr/good.py @@ -0,0 +1,2 @@ +if apples := 2: + print("God apples!") diff --git a/lib/common/pylint_data/messages/consider-using-augmented-assign/bad.py b/lib/common/pylint_data/messages/consider-using-augmented-assign/bad.py new file mode 100644 index 0000000..90b8931 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-augmented-assign/bad.py @@ -0,0 +1,2 @@ +x = 1 +x = x + 1 # [consider-using-augmented-assign] diff --git a/lib/common/pylint_data/messages/consider-using-augmented-assign/good.py b/lib/common/pylint_data/messages/consider-using-augmented-assign/good.py new file mode 100644 index 0000000..3e34f6b --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-augmented-assign/good.py @@ -0,0 +1,2 @@ +x = 1 +x += 1 diff --git a/lib/common/pylint_data/messages/consider-using-dict-comprehension/bad.py b/lib/common/pylint_data/messages/consider-using-dict-comprehension/bad.py new file mode 100644 index 0000000..d9b02c7 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-dict-comprehension/bad.py @@ -0,0 +1,4 @@ +NUMBERS = [1, 2, 3] + +# +1: [consider-using-dict-comprehension] +DOUBLED_NUMBERS = dict([(number, number * 2) for number in NUMBERS]) diff --git a/lib/common/pylint_data/messages/consider-using-dict-comprehension/details.md b/lib/common/pylint_data/messages/consider-using-dict-comprehension/details.md new file mode 100644 index 0000000..590094e --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-dict-comprehension/details.md @@ -0,0 +1,2 @@ +[pyupgrade](https://github.com/asottile/pyupgrade) or +[ruff](https://docs.astral.sh/ruff/) can fix this issue automatically. diff --git a/lib/common/pylint_data/messages/consider-using-dict-comprehension/good.py b/lib/common/pylint_data/messages/consider-using-dict-comprehension/good.py new file mode 100644 index 0000000..5f91d56 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-dict-comprehension/good.py @@ -0,0 +1,3 @@ +NUMBERS = [1, 2, 3] + +DOUBLED_NUMBERS = {number: number * 2 for number in NUMBERS} diff --git a/lib/common/pylint_data/messages/consider-using-dict-items/bad.py b/lib/common/pylint_data/messages/consider-using-dict-items/bad.py new file mode 100644 index 0000000..ce8d4d4 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-dict-items/bad.py @@ -0,0 +1,10 @@ +ORCHESTRA = { + "violin": "strings", + "oboe": "woodwind", + "tuba": "brass", + "gong": "percussion", +} + + +for instrument in ORCHESTRA: # [consider-using-dict-items] + print(f"{instrument}: {ORCHESTRA[instrument]}") diff --git a/lib/common/pylint_data/messages/consider-using-dict-items/good.py b/lib/common/pylint_data/messages/consider-using-dict-items/good.py new file mode 100644 index 0000000..f719b1a --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-dict-items/good.py @@ -0,0 +1,10 @@ +ORCHESTRA = { + "violin": "strings", + "oboe": "woodwind", + "tuba": "brass", + "gong": "percussion", +} + + +for instrument, section in ORCHESTRA.items(): + print(f"{instrument}: {section}") diff --git a/lib/common/pylint_data/messages/consider-using-enumerate/bad.py b/lib/common/pylint_data/messages/consider-using-enumerate/bad.py new file mode 100644 index 0000000..0beee2e --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-enumerate/bad.py @@ -0,0 +1,4 @@ +seasons = ["Spring", "Summer", "Fall", "Winter"] + +for i in range(len(seasons)): # [consider-using-enumerate] + print(i, seasons[i]) diff --git a/lib/common/pylint_data/messages/consider-using-enumerate/good.py b/lib/common/pylint_data/messages/consider-using-enumerate/good.py new file mode 100644 index 0000000..b078e48 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-enumerate/good.py @@ -0,0 +1,4 @@ +seasons = ["Spring", "Summer", "Fall", "Winter"] + +for i, season in enumerate(seasons): + print(i, season) diff --git a/lib/common/pylint_data/messages/consider-using-f-string/bad.py b/lib/common/pylint_data/messages/consider-using-f-string/bad.py new file mode 100644 index 0000000..26da6a1 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-f-string/bad.py @@ -0,0 +1,16 @@ +from string import Template + +menu = ("eggs", "spam", 42.4) + +old_order = "%s and %s: %.2f ¤" % menu # [consider-using-f-string] +beginner_order = menu[0] + " and " + menu[1] + ": " + str(menu[2]) + " ¤" +joined_order = " and ".join(menu[:2]) +# +1: [consider-using-f-string] +format_order = "{} and {}: {:0.2f} ¤".format(menu[0], menu[1], menu[2]) +# +1: [consider-using-f-string] +named_format_order = "{eggs} and {spam}: {price:0.2f} ¤".format( + eggs=menu[0], spam=menu[1], price=menu[2] +) +template_order = Template("$eggs and $spam: $price ¤").substitute( + eggs=menu[0], spam=menu[1], price=menu[2] +) diff --git a/lib/common/pylint_data/messages/consider-using-f-string/details.md b/lib/common/pylint_data/messages/consider-using-f-string/details.md new file mode 100644 index 0000000..0616267 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-f-string/details.md @@ -0,0 +1,7 @@ +Formatted string literals (f-strings) give a concise, consistent syntax +that can replace most use cases for the `%` formatting operator, +`str.format()` and `string.Template`. + +F-strings also perform better than alternatives; see [this +tweet](https://twitter.com/raymondh/status/1205969258800275456) for a +simple example. diff --git a/lib/common/pylint_data/messages/consider-using-f-string/good.py b/lib/common/pylint_data/messages/consider-using-f-string/good.py new file mode 100644 index 0000000..f7913fa --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-f-string/good.py @@ -0,0 +1,3 @@ +menu = ("eggs", "spam", 42.4) + +f_string_order = f"{menu[0]} and {menu[1]}: {menu[2]:0.2f} ¤" diff --git a/lib/common/pylint_data/messages/consider-using-from-import/bad.py b/lib/common/pylint_data/messages/consider-using-from-import/bad.py new file mode 100644 index 0000000..ff2f489 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-from-import/bad.py @@ -0,0 +1 @@ +import os.path as path # [consider-using-from-import] diff --git a/lib/common/pylint_data/messages/consider-using-from-import/good.py b/lib/common/pylint_data/messages/consider-using-from-import/good.py new file mode 100644 index 0000000..cfd7c68 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-from-import/good.py @@ -0,0 +1 @@ +from os import path diff --git a/lib/common/pylint_data/messages/consider-using-generator/bad.py b/lib/common/pylint_data/messages/consider-using-generator/bad.py new file mode 100644 index 0000000..260ec97 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-generator/bad.py @@ -0,0 +1,5 @@ +list([0 for y in list(range(10))]) # [consider-using-generator] +tuple([0 for y in list(range(10))]) # [consider-using-generator] +sum([y**2 for y in list(range(10))]) # [consider-using-generator] +max([y**2 for y in list(range(10))]) # [consider-using-generator] +min([y**2 for y in list(range(10))]) # [consider-using-generator] diff --git a/lib/common/pylint_data/messages/consider-using-generator/details.md b/lib/common/pylint_data/messages/consider-using-generator/details.md new file mode 100644 index 0000000..47f989d --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-generator/details.md @@ -0,0 +1,7 @@ +Removing `[]` inside calls that can use containers or generators should +be considered for performance reasons since a generator will have an +upfront cost to pay. The performance will be better if you are working +with long lists or sets. + +For `max`, `min` and `sum` using a generator is also recommended by +pep289. diff --git a/lib/common/pylint_data/messages/consider-using-generator/good.py b/lib/common/pylint_data/messages/consider-using-generator/good.py new file mode 100644 index 0000000..6674167 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-generator/good.py @@ -0,0 +1,5 @@ +list(0 for y in list(range(10))) +tuple(0 for y in list(range(10))) +sum(y**2 for y in list(range(10))) +max(y**2 for y in list(range(10))) +min(y**2 for y in list(range(10))) diff --git a/lib/common/pylint_data/messages/consider-using-generator/related.md b/lib/common/pylint_data/messages/consider-using-generator/related.md new file mode 100644 index 0000000..0e76837 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-generator/related.md @@ -0,0 +1,5 @@ +- [PEP 289](https://peps.python.org/pep-0289/) +- [Benchmark and discussion for + any/all/list/tuple](https://github.com/pylint-dev/pylint/pull/3309#discussion_r576683109) +- [Benchmark and discussion for + sum/max/min](https://github.com/pylint-dev/pylint/pull/6595#issuecomment-1125704244) diff --git a/lib/common/pylint_data/messages/consider-using-get/bad.py b/lib/common/pylint_data/messages/consider-using-get/bad.py new file mode 100644 index 0000000..b820c56 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-get/bad.py @@ -0,0 +1,6 @@ +knights = {"Gallahad": "the pure", "Robin": "the brave"} + +if "Gallahad" in knights: # [consider-using-get] + DESCRIPTION = knights["Gallahad"] +else: + DESCRIPTION = "" diff --git a/lib/common/pylint_data/messages/consider-using-get/good.py b/lib/common/pylint_data/messages/consider-using-get/good.py new file mode 100644 index 0000000..eba9976 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-get/good.py @@ -0,0 +1,3 @@ +knights = {"Gallahad": "the pure", "Robin": "the brave"} + +description = knights.get("Gallahad", "") diff --git a/lib/common/pylint_data/messages/consider-using-in/bad.py b/lib/common/pylint_data/messages/consider-using-in/bad.py new file mode 100644 index 0000000..81eddb7 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-in/bad.py @@ -0,0 +1,3 @@ +def fruit_is_round(fruit): + # +1: [consider-using-in] + return fruit == "apple" or fruit == "orange" or fruit == "melon" diff --git a/lib/common/pylint_data/messages/consider-using-in/good.py b/lib/common/pylint_data/messages/consider-using-in/good.py new file mode 100644 index 0000000..bc308fa --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-in/good.py @@ -0,0 +1,2 @@ +def fruit_is_round(fruit): + return fruit in {"apple", "orange", "melon"} diff --git a/lib/common/pylint_data/messages/consider-using-join/bad.py b/lib/common/pylint_data/messages/consider-using-join/bad.py new file mode 100644 index 0000000..5d5a32f --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-join/bad.py @@ -0,0 +1,8 @@ +def fruits_to_string(fruits): + formatted_fruit = "" + for fruit in fruits: + formatted_fruit += fruit # [consider-using-join] + return formatted_fruit + + +print(fruits_to_string(["apple", "pear", "peach"])) diff --git a/lib/common/pylint_data/messages/consider-using-join/good.py b/lib/common/pylint_data/messages/consider-using-join/good.py new file mode 100644 index 0000000..1ee43ed --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-join/good.py @@ -0,0 +1 @@ +print("".join(["apple", "pear", "peach"])) diff --git a/lib/common/pylint_data/messages/consider-using-max-builtin/bad.py b/lib/common/pylint_data/messages/consider-using-max-builtin/bad.py new file mode 100644 index 0000000..3e66cee --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-max-builtin/bad.py @@ -0,0 +1,7 @@ +def get_max(value1, value2): + if value1 < value2: # [consider-using-max-builtin] + value1 = value2 + return value1 + + +print(get_max(1, 2)) diff --git a/lib/common/pylint_data/messages/consider-using-max-builtin/good.py b/lib/common/pylint_data/messages/consider-using-max-builtin/good.py new file mode 100644 index 0000000..90af411 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-max-builtin/good.py @@ -0,0 +1 @@ +print(max(1, 2)) diff --git a/lib/common/pylint_data/messages/consider-using-min-builtin/bad.py b/lib/common/pylint_data/messages/consider-using-min-builtin/bad.py new file mode 100644 index 0000000..007371a --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-min-builtin/bad.py @@ -0,0 +1,7 @@ +def get_min(value1, value2): + if value1 > value2: # [consider-using-min-builtin] + value1 = value2 + return value1 + + +print(get_min(1, 2)) diff --git a/lib/common/pylint_data/messages/consider-using-min-builtin/good.py b/lib/common/pylint_data/messages/consider-using-min-builtin/good.py new file mode 100644 index 0000000..1995129 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-min-builtin/good.py @@ -0,0 +1 @@ +print(min(1, 2)) diff --git a/lib/common/pylint_data/messages/consider-using-namedtuple-or-dataclass/bad.py b/lib/common/pylint_data/messages/consider-using-namedtuple-or-dataclass/bad.py new file mode 100644 index 0000000..d1f3eba --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-namedtuple-or-dataclass/bad.py @@ -0,0 +1,14 @@ +FELIDAES = { # [consider-using-namedtuple-or-dataclass] + "The queen's cymric, fragile furry friend": { + "tail_length_cm": 1, + "paws": 4, + "eyes": 2, + "Elizabethan collar": 1, + }, + "Rackat the red, terror of the sea": { + "tail_length_cm": 13, + "paws": 3, + "eyes": 1, + "Red Hat": 1, + }, +} diff --git a/lib/common/pylint_data/messages/consider-using-namedtuple-or-dataclass/good.py b/lib/common/pylint_data/messages/consider-using-namedtuple-or-dataclass/good.py new file mode 100644 index 0000000..b13713f --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-namedtuple-or-dataclass/good.py @@ -0,0 +1,18 @@ +from typing import NamedTuple + + +class FelidaeCharacteristics(NamedTuple): + tail_length_cm: int + paws: int + eyes: int + hat: str | None + + +FELIDAES = { + "The queen's cymric, fragile furry friend": FelidaeCharacteristics( + tail_length_cm=1, paws=4, eyes=2, hat="Elizabethan collar" + ), + "Rackat the red, terror of the sea": FelidaeCharacteristics( + tail_length_cm=21, paws=3, eyes=1, hat="Red Hat" + ), +} diff --git a/lib/common/pylint_data/messages/consider-using-set-comprehension/bad.py b/lib/common/pylint_data/messages/consider-using-set-comprehension/bad.py new file mode 100644 index 0000000..ffdc9e5 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-set-comprehension/bad.py @@ -0,0 +1,4 @@ +NUMBERS = [1, 2, 2, 3, 4, 4] + +# +1: [consider-using-set-comprehension] +UNIQUE_EVEN_NUMBERS = set([number for number in NUMBERS if number % 2 == 0]) diff --git a/lib/common/pylint_data/messages/consider-using-set-comprehension/details.md b/lib/common/pylint_data/messages/consider-using-set-comprehension/details.md new file mode 100644 index 0000000..590094e --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-set-comprehension/details.md @@ -0,0 +1,2 @@ +[pyupgrade](https://github.com/asottile/pyupgrade) or +[ruff](https://docs.astral.sh/ruff/) can fix this issue automatically. diff --git a/lib/common/pylint_data/messages/consider-using-set-comprehension/good.py b/lib/common/pylint_data/messages/consider-using-set-comprehension/good.py new file mode 100644 index 0000000..4914fc2 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-set-comprehension/good.py @@ -0,0 +1,3 @@ +NUMBERS = [1, 2, 2, 3, 4, 4] + +UNIQUE_EVEN_NUMBERS = {number for number in NUMBERS if number % 2 == 0} diff --git a/lib/common/pylint_data/messages/consider-using-sys-exit/bad.py b/lib/common/pylint_data/messages/consider-using-sys-exit/bad.py new file mode 100644 index 0000000..1acc22a --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-sys-exit/bad.py @@ -0,0 +1,4 @@ +if __name__ == "__main__": + user = input("Enter user name: ") + print(f"Hello, {user}") + exit(0) # [consider-using-sys-exit] diff --git a/lib/common/pylint_data/messages/consider-using-sys-exit/good.py b/lib/common/pylint_data/messages/consider-using-sys-exit/good.py new file mode 100644 index 0000000..acacd34 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-sys-exit/good.py @@ -0,0 +1,6 @@ +import sys + +if __name__ == "__main__": + user = input("Enter user name: ") + print(f"Hello, {user}") + sys.exit(0) diff --git a/lib/common/pylint_data/messages/consider-using-ternary/bad.py b/lib/common/pylint_data/messages/consider-using-ternary/bad.py new file mode 100644 index 0000000..bd69aa9 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-ternary/bad.py @@ -0,0 +1,2 @@ +x, y = 1, 2 +maximum = x >= y and x or y # [consider-using-ternary] diff --git a/lib/common/pylint_data/messages/consider-using-ternary/good.py b/lib/common/pylint_data/messages/consider-using-ternary/good.py new file mode 100644 index 0000000..bcb8446 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-ternary/good.py @@ -0,0 +1,2 @@ +x, y = 1, 2 +maximum = x if x >= y else y diff --git a/lib/common/pylint_data/messages/consider-using-tuple/bad.py b/lib/common/pylint_data/messages/consider-using-tuple/bad.py new file mode 100644 index 0000000..099efb9 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-tuple/bad.py @@ -0,0 +1,2 @@ +for i in [1, 2, 3]: # [consider-using-tuple] + print(i) diff --git a/lib/common/pylint_data/messages/consider-using-tuple/good.py b/lib/common/pylint_data/messages/consider-using-tuple/good.py new file mode 100644 index 0000000..c0e2e70 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-tuple/good.py @@ -0,0 +1,2 @@ +for i in (1, 2, 3): + print(i) diff --git a/lib/common/pylint_data/messages/consider-using-with/bad.py b/lib/common/pylint_data/messages/consider-using-with/bad.py new file mode 100644 index 0000000..b6a83c4 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-with/bad.py @@ -0,0 +1,5 @@ +file = open("apple.txt", "r", encoding="utf8") # [consider-using-with] +contents = file.read() +file.close() + +contents = open("apple.txt", "r", encoding="utf8").read() # [consider-using-with] diff --git a/lib/common/pylint_data/messages/consider-using-with/details.md b/lib/common/pylint_data/messages/consider-using-with/details.md new file mode 100644 index 0000000..6148fca --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-with/details.md @@ -0,0 +1,10 @@ +Calling `write()` without using the `with` keyword or calling `close()` +might result in the arguments of `write()` not being completely written +to the disk, even if the program exits successfully. + +This message applies to callables of Python\'s stdlib which can be +replaced by a `with` statement. It is suppressed in the following cases: + +- the call is located inside a context manager +- the call result is returned from the enclosing function +- the call result is used in a `with` statement itself diff --git a/lib/common/pylint_data/messages/consider-using-with/good.py b/lib/common/pylint_data/messages/consider-using-with/good.py new file mode 100644 index 0000000..3994259 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-with/good.py @@ -0,0 +1,2 @@ +with open("apple.txt", "r", encoding="utf8") as file: + contents = file.read() diff --git a/lib/common/pylint_data/messages/consider-using-with/related.md b/lib/common/pylint_data/messages/consider-using-with/related.md new file mode 100644 index 0000000..51b3156 --- /dev/null +++ b/lib/common/pylint_data/messages/consider-using-with/related.md @@ -0,0 +1,7 @@ +- [Python doc: Reading and writing + files](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files) +- [PEP 343](https://peps.python.org/pep-0343/) +- [Context managers in + Python](https://johnlekberg.com/blog/2020-10-11-ctx-manage.html) by + John Lekberg +- [Rationale](https://stackoverflow.com/a/73181877/2519059) diff --git a/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/bad.py b/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/bad.py new file mode 100644 index 0000000..b2072f7 --- /dev/null +++ b/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/bad.py @@ -0,0 +1,14 @@ +import contextlib + + +@contextlib.contextmanager +def cm(): + contextvar = "acquired context" + print("cm enter") + yield contextvar + print("cm exit") + + +def genfunc_with_cm(): + with cm() as context: # [contextmanager-generator-missing-cleanup] + yield context * 2 diff --git a/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/details.md b/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/details.md new file mode 100644 index 0000000..657b636 --- /dev/null +++ b/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/details.md @@ -0,0 +1,35 @@ +Instantiating and using a contextmanager inside a generator function can +result in unexpected behavior if there is an expectation that the +context is only available for the generator function. In the case that +the generator is not closed or destroyed then the context manager is +held suspended as is. + +This message warns on the generator function instead of the +contextmanager function because the ways to use a contextmanager are +many. A contextmanager can be used as a decorator (which immediately has +`__enter__`/`__exit__` applied) and the use of `as ...` or discard of +the return value also implies whether the context needs cleanup or not. +So for this message, warning the invoker of the contextmanager is +important. + +The check can create false positives if `yield` is used inside an +`if-else` block without custom cleanup. Use `pylint: disable` for these. + +``` python +from contextlib import contextmanager + +@contextmanager +def good_cm_no_cleanup(): + contextvar = "acquired context" + print("cm enter") + if some_condition: + yield contextvar + else: + yield contextvar + + +def good_cm_no_cleanup_genfunc(): + # pylint: disable-next=contextmanager-generator-missing-cleanup + with good_cm_no_cleanup() as context: + yield context * 2 +``` diff --git a/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/good.py b/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/good.py new file mode 100644 index 0000000..2287e86 --- /dev/null +++ b/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/good.py @@ -0,0 +1,61 @@ +import contextlib + + +@contextlib.contextmanager +def good_cm_except(): + contextvar = "acquired context" + print("good cm enter") + try: + yield contextvar + except GeneratorExit: + print("good cm exit") + + +def genfunc_with_cm(): + with good_cm_except() as context: + yield context * 2 + + +def genfunc_with_discard(): + with good_cm_except(): + yield "discarded" + + +@contextlib.contextmanager +def good_cm_yield_none(): + print("good cm enter") + yield + print("good cm exit") + + +def genfunc_with_none_yield(): + with good_cm_yield_none() as var: + print(var) + yield "constant yield" + + +@contextlib.contextmanager +def good_cm_finally(): + contextvar = "acquired context" + print("good cm enter") + try: + yield contextvar + finally: + print("good cm exit") + + +def good_cm_finally_genfunc(): + with good_cm_finally() as context: + yield context * 2 + + +@contextlib.contextmanager +def good_cm_no_cleanup(): + contextvar = "acquired context" + print("cm enter") + yield contextvar + + +def good_cm_no_cleanup_genfunc(): + with good_cm_no_cleanup() as context: + yield context * 2 diff --git a/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/related.md b/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/related.md new file mode 100644 index 0000000..fef62cd --- /dev/null +++ b/lib/common/pylint_data/messages/contextmanager-generator-missing-cleanup/related.md @@ -0,0 +1,3 @@ +- [Rationale](https://discuss.python.org/t/preventing-yield-inside-certain-context-managers/1091) +- [CPython + Issue](https://github.com/python/cpython/issues/81924#issuecomment-1093830682) diff --git a/lib/common/pylint_data/messages/continue-in-finally/bad.py b/lib/common/pylint_data/messages/continue-in-finally/bad.py new file mode 100644 index 0000000..190463f --- /dev/null +++ b/lib/common/pylint_data/messages/continue-in-finally/bad.py @@ -0,0 +1,5 @@ +while True: + try: + pass + finally: + continue # [continue-in-finally] diff --git a/lib/common/pylint_data/messages/continue-in-finally/good.py b/lib/common/pylint_data/messages/continue-in-finally/good.py new file mode 100644 index 0000000..6b14ef4 --- /dev/null +++ b/lib/common/pylint_data/messages/continue-in-finally/good.py @@ -0,0 +1,7 @@ +while True: + try: + pass + except ValueError: + pass + else: + continue diff --git a/lib/common/pylint_data/messages/continue-in-finally/related.md b/lib/common/pylint_data/messages/continue-in-finally/related.md new file mode 100644 index 0000000..8def6ef --- /dev/null +++ b/lib/common/pylint_data/messages/continue-in-finally/related.md @@ -0,0 +1,4 @@ +- [Python 3 docs \'finally\' + clause](https://docs.python.org/3/reference/compound_stmts.html#finally-clause) +- [PEP 765 - Disallow return/break/continue that exit a finally + block](https://peps.python.org/pep-0765/) diff --git a/lib/common/pylint_data/messages/cyclic-import/bad.py b/lib/common/pylint_data/messages/cyclic-import/bad.py new file mode 100644 index 0000000..f8dc8ab --- /dev/null +++ b/lib/common/pylint_data/messages/cyclic-import/bad.py @@ -0,0 +1,9 @@ +def count_to_one(): + return 1 + + + +def count_to_three(): + from .bad2 import count_to_two # [imports from bad2.py, which in turn imports from this module] + + return count_to_two() + 1 diff --git a/lib/common/pylint_data/messages/cyclic-import/details.md b/lib/common/pylint_data/messages/cyclic-import/details.md new file mode 100644 index 0000000..fdc321d --- /dev/null +++ b/lib/common/pylint_data/messages/cyclic-import/details.md @@ -0,0 +1,3 @@ +The good code is just an example. There are various strategies to +resolving cyclic imports and the best choice relies heavily on the +context of the code and the affected modules. diff --git a/lib/common/pylint_data/messages/cyclic-import/good.py b/lib/common/pylint_data/messages/cyclic-import/good.py new file mode 100644 index 0000000..7924246 --- /dev/null +++ b/lib/common/pylint_data/messages/cyclic-import/good.py @@ -0,0 +1,10 @@ +def count_to_one(): + return 1 + + +def count_to_two(): + return count_to_one() + 1 + + +def count_to_three(): + return count_to_two() + 1 diff --git a/lib/common/pylint_data/messages/dangerous-default-value/bad.py b/lib/common/pylint_data/messages/dangerous-default-value/bad.py new file mode 100644 index 0000000..6ce1223 --- /dev/null +++ b/lib/common/pylint_data/messages/dangerous-default-value/bad.py @@ -0,0 +1,3 @@ +def whats_on_the_telly(penguin=[]): # [dangerous-default-value] + penguin.append("property of the zoo") + return penguin diff --git a/lib/common/pylint_data/messages/dangerous-default-value/details.md b/lib/common/pylint_data/messages/dangerous-default-value/details.md new file mode 100644 index 0000000..3b8ba04 --- /dev/null +++ b/lib/common/pylint_data/messages/dangerous-default-value/details.md @@ -0,0 +1,8 @@ +With a mutable default value, with each call the default value is +modified, i.e.: + +``` python +whats_on_the_telly() # ["property of the zoo"] +whats_on_the_telly() # ["property of the zoo", "property of the zoo"] +whats_on_the_telly() # ["property of the zoo", "property of the zoo", "property of the zoo"] +``` diff --git a/lib/common/pylint_data/messages/dangerous-default-value/good.py b/lib/common/pylint_data/messages/dangerous-default-value/good.py new file mode 100644 index 0000000..605c3ce --- /dev/null +++ b/lib/common/pylint_data/messages/dangerous-default-value/good.py @@ -0,0 +1,5 @@ +def whats_on_the_telly(penguin=None): + if penguin is None: + penguin = [] + penguin.append("property of the zoo") + return penguin diff --git a/lib/common/pylint_data/messages/declare-non-slot/bad.py b/lib/common/pylint_data/messages/declare-non-slot/bad.py new file mode 100644 index 0000000..5e39d47 --- /dev/null +++ b/lib/common/pylint_data/messages/declare-non-slot/bad.py @@ -0,0 +1,5 @@ +class Student: + __slots__ = ("name",) + + name: str + surname: str # [declare-non-slot] diff --git a/lib/common/pylint_data/messages/declare-non-slot/good.py b/lib/common/pylint_data/messages/declare-non-slot/good.py new file mode 100644 index 0000000..1ca1de1 --- /dev/null +++ b/lib/common/pylint_data/messages/declare-non-slot/good.py @@ -0,0 +1,5 @@ +class Student: + __slots__ = ("name", "surname") + + name: str + surname: str diff --git a/lib/common/pylint_data/messages/deprecated-argument/bad.py b/lib/common/pylint_data/messages/deprecated-argument/bad.py new file mode 100644 index 0000000..d0b0c25 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-argument/bad.py @@ -0,0 +1 @@ +int(x=1) # [deprecated-argument] diff --git a/lib/common/pylint_data/messages/deprecated-argument/details.md b/lib/common/pylint_data/messages/deprecated-argument/details.md new file mode 100644 index 0000000..1288fa4 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-argument/details.md @@ -0,0 +1,2 @@ +The actual replacement needs to be studied on a case by case basis by +reading the deprecation warning or the release notes. diff --git a/lib/common/pylint_data/messages/deprecated-argument/good.py b/lib/common/pylint_data/messages/deprecated-argument/good.py new file mode 100644 index 0000000..8269369 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-argument/good.py @@ -0,0 +1 @@ +int(1) diff --git a/lib/common/pylint_data/messages/deprecated-attribute/bad.py b/lib/common/pylint_data/messages/deprecated-attribute/bad.py new file mode 100644 index 0000000..ccd4090 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-attribute/bad.py @@ -0,0 +1,4 @@ +from configparser import ParsingError + +err = ParsingError("filename") +source = err.filename # [deprecated-attribute] diff --git a/lib/common/pylint_data/messages/deprecated-attribute/details.md b/lib/common/pylint_data/messages/deprecated-attribute/details.md new file mode 100644 index 0000000..1288fa4 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-attribute/details.md @@ -0,0 +1,2 @@ +The actual replacement needs to be studied on a case by case basis by +reading the deprecation warning or the release notes. diff --git a/lib/common/pylint_data/messages/deprecated-attribute/good.py b/lib/common/pylint_data/messages/deprecated-attribute/good.py new file mode 100644 index 0000000..492cd80 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-attribute/good.py @@ -0,0 +1,4 @@ +from configparser import ParsingError + +err = ParsingError("filename") +source = err.source diff --git a/lib/common/pylint_data/messages/deprecated-class/bad.py b/lib/common/pylint_data/messages/deprecated-class/bad.py new file mode 100644 index 0000000..ab96b50 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-class/bad.py @@ -0,0 +1 @@ +from collections import Iterable # [deprecated-class] diff --git a/lib/common/pylint_data/messages/deprecated-class/details.md b/lib/common/pylint_data/messages/deprecated-class/details.md new file mode 100644 index 0000000..1288fa4 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-class/details.md @@ -0,0 +1,2 @@ +The actual replacement needs to be studied on a case by case basis by +reading the deprecation warning or the release notes. diff --git a/lib/common/pylint_data/messages/deprecated-class/good.py b/lib/common/pylint_data/messages/deprecated-class/good.py new file mode 100644 index 0000000..775057c --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-class/good.py @@ -0,0 +1 @@ +from collections.abc import Iterable diff --git a/lib/common/pylint_data/messages/deprecated-decorator/bad.py b/lib/common/pylint_data/messages/deprecated-decorator/bad.py new file mode 100644 index 0000000..afae4c7 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-decorator/bad.py @@ -0,0 +1,7 @@ +import abc + + +class Animal: + @abc.abstractclassmethod # [deprecated-decorator] + def breath(cls): + pass diff --git a/lib/common/pylint_data/messages/deprecated-decorator/details.md b/lib/common/pylint_data/messages/deprecated-decorator/details.md new file mode 100644 index 0000000..1288fa4 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-decorator/details.md @@ -0,0 +1,2 @@ +The actual replacement needs to be studied on a case by case basis by +reading the deprecation warning or the release notes. diff --git a/lib/common/pylint_data/messages/deprecated-decorator/good.py b/lib/common/pylint_data/messages/deprecated-decorator/good.py new file mode 100644 index 0000000..3388fde --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-decorator/good.py @@ -0,0 +1,8 @@ +import abc + + +class Animal: + @abc.classmethod + @abc.abstractmethod + def breath(cls): + pass diff --git a/lib/common/pylint_data/messages/deprecated-method/bad.py b/lib/common/pylint_data/messages/deprecated-method/bad.py new file mode 100644 index 0000000..78870f3 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-method/bad.py @@ -0,0 +1,3 @@ +import logging + +logging.warn("I'm coming, world !") # [deprecated-method] diff --git a/lib/common/pylint_data/messages/deprecated-method/details.md b/lib/common/pylint_data/messages/deprecated-method/details.md new file mode 100644 index 0000000..1288fa4 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-method/details.md @@ -0,0 +1,2 @@ +The actual replacement needs to be studied on a case by case basis by +reading the deprecation warning or the release notes. diff --git a/lib/common/pylint_data/messages/deprecated-method/good.py b/lib/common/pylint_data/messages/deprecated-method/good.py new file mode 100644 index 0000000..e54ea0e --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-method/good.py @@ -0,0 +1,3 @@ +import logging + +logging.warning("I'm coming, world !") diff --git a/lib/common/pylint_data/messages/deprecated-module/bad.py b/lib/common/pylint_data/messages/deprecated-module/bad.py new file mode 100644 index 0000000..fd17f13 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-module/bad.py @@ -0,0 +1,3 @@ +import distutils # [deprecated-module] + +import whatever_you_want # [deprecated-module] diff --git a/lib/common/pylint_data/messages/deprecated-module/details.md b/lib/common/pylint_data/messages/deprecated-module/details.md new file mode 100644 index 0000000..1288fa4 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-module/details.md @@ -0,0 +1,2 @@ +The actual replacement needs to be studied on a case by case basis by +reading the deprecation warning or the release notes. diff --git a/lib/common/pylint_data/messages/deprecated-module/good.py b/lib/common/pylint_data/messages/deprecated-module/good.py new file mode 100644 index 0000000..014a76c --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-module/good.py @@ -0,0 +1,2 @@ +import setuptools +import whatever_replacement_you_want diff --git a/lib/common/pylint_data/messages/deprecated-pragma/bad.py b/lib/common/pylint_data/messages/deprecated-pragma/bad.py new file mode 100644 index 0000000..133bf89 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-pragma/bad.py @@ -0,0 +1 @@ +# pylint: disable-msg=eval-used # [deprecated-pragma] diff --git a/lib/common/pylint_data/messages/deprecated-pragma/good.py b/lib/common/pylint_data/messages/deprecated-pragma/good.py new file mode 100644 index 0000000..8aebe0b --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-pragma/good.py @@ -0,0 +1 @@ +# pylint: disable = eval-used diff --git a/lib/common/pylint_data/messages/deprecated-typing-alias/bad.py b/lib/common/pylint_data/messages/deprecated-typing-alias/bad.py new file mode 100644 index 0000000..cde5564 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-typing-alias/bad.py @@ -0,0 +1,3 @@ +import typing + +item_to_number_of_item: typing.Dict[str, int] # [deprecated-typing-alias] diff --git a/lib/common/pylint_data/messages/deprecated-typing-alias/good.py b/lib/common/pylint_data/messages/deprecated-typing-alias/good.py new file mode 100644 index 0000000..951fa52 --- /dev/null +++ b/lib/common/pylint_data/messages/deprecated-typing-alias/good.py @@ -0,0 +1 @@ +item_to_number_of_item: dict[str, int] diff --git a/lib/common/pylint_data/messages/dict-init-mutate/bad.py b/lib/common/pylint_data/messages/dict-init-mutate/bad.py new file mode 100644 index 0000000..d7db9c5 --- /dev/null +++ b/lib/common/pylint_data/messages/dict-init-mutate/bad.py @@ -0,0 +1,3 @@ +fruit_prices = {} # [dict-init-mutate] +fruit_prices["apple"] = 1 +fruit_prices["banana"] = 10 diff --git a/lib/common/pylint_data/messages/dict-init-mutate/good.py b/lib/common/pylint_data/messages/dict-init-mutate/good.py new file mode 100644 index 0000000..02137f2 --- /dev/null +++ b/lib/common/pylint_data/messages/dict-init-mutate/good.py @@ -0,0 +1 @@ +fruit_prices = {"apple": 1, "banana": 10} diff --git a/lib/common/pylint_data/messages/dict-iter-missing-items/bad.py b/lib/common/pylint_data/messages/dict-iter-missing-items/bad.py new file mode 100644 index 0000000..52efa3a --- /dev/null +++ b/lib/common/pylint_data/messages/dict-iter-missing-items/bad.py @@ -0,0 +1,3 @@ +data = {"Paris": 2_165_423, "New York City": 8_804_190, "Tokyo": 13_988_129} +for city, population in data: # [dict-iter-missing-items] + print(f"{city} has population {population}.") diff --git a/lib/common/pylint_data/messages/dict-iter-missing-items/good.py b/lib/common/pylint_data/messages/dict-iter-missing-items/good.py new file mode 100644 index 0000000..3eb97f9 --- /dev/null +++ b/lib/common/pylint_data/messages/dict-iter-missing-items/good.py @@ -0,0 +1,3 @@ +data = {"Paris": 2_165_423, "New York City": 8_804_190, "Tokyo": 13_988_129} +for city, population in data.items(): + print(f"{city} has population {population}.") diff --git a/lib/common/pylint_data/messages/differing-param-doc/bad.py b/lib/common/pylint_data/messages/differing-param-doc/bad.py new file mode 100644 index 0000000..2a48d82 --- /dev/null +++ b/lib/common/pylint_data/messages/differing-param-doc/bad.py @@ -0,0 +1,8 @@ +def add(x, y): # [differing-param-doc] + """Add two numbers. + + :param int x: x value. + :param int z: z value. + """ + + return x + y diff --git a/lib/common/pylint_data/messages/differing-param-doc/good.py b/lib/common/pylint_data/messages/differing-param-doc/good.py new file mode 100644 index 0000000..8deac67 --- /dev/null +++ b/lib/common/pylint_data/messages/differing-param-doc/good.py @@ -0,0 +1,8 @@ +def add(x, y): + """Add two numbers. + + :param int x: x value. + :param int y: y value. + """ + + return x + y diff --git a/lib/common/pylint_data/messages/differing-type-doc/bad.py b/lib/common/pylint_data/messages/differing-type-doc/bad.py new file mode 100644 index 0000000..18d41a9 --- /dev/null +++ b/lib/common/pylint_data/messages/differing-type-doc/bad.py @@ -0,0 +1,8 @@ +def add(x: int, y: int): # [differing-type-doc] + """Add two numbers. + + :param int xy: x value. + :param str y: y value. + """ + + return x + y diff --git a/lib/common/pylint_data/messages/differing-type-doc/good.py b/lib/common/pylint_data/messages/differing-type-doc/good.py new file mode 100644 index 0000000..8deac67 --- /dev/null +++ b/lib/common/pylint_data/messages/differing-type-doc/good.py @@ -0,0 +1,8 @@ +def add(x, y): + """Add two numbers. + + :param int x: x value. + :param int y: y value. + """ + + return x + y diff --git a/lib/common/pylint_data/messages/disallowed-name/bad.py b/lib/common/pylint_data/messages/disallowed-name/bad.py new file mode 100644 index 0000000..272c7cb --- /dev/null +++ b/lib/common/pylint_data/messages/disallowed-name/bad.py @@ -0,0 +1,6 @@ +def foo(): # [disallowed-name] + print("apples") + +x = 15 # [disallowed-name] +y = 12 # [disallowed-name] +result = x + y \ No newline at end of file diff --git a/lib/common/pylint_data/messages/disallowed-name/details.md b/lib/common/pylint_data/messages/disallowed-name/details.md new file mode 100644 index 0000000..fc5c5bd --- /dev/null +++ b/lib/common/pylint_data/messages/disallowed-name/details.md @@ -0,0 +1,15 @@ +## Good names: + +1. Follow existing naming conventions for the codebase or framework you are working in, and are in the language (_e.g. Spanish, French, English_) that the codebase or framework uses. +2. Are clear and concise. +3. Are as long as necessary to communicate, but no longer. + - It is OK to be verbose, but it has gone too far if the variable name is so long that it starts to interfere with easily reading the code. +4. Avoid the use of single letters or frequent abbreviations. + - Err on the side of being descriptive. Other programmers (and your future self) will thank you for your clarity. + - Remember that context _matters_. The context of your variable (or function or class) name is likely to be lost if the code is longer than about 5 lines. Even within that 5-line limit, those reading your code for the first time might have difficulty following. + - The memory/time saved in typing something short but obscure is more than negated by someone not understanding the code at first glance. +5. Avoid what is known as [Hungarian notation](https://en.wikipedia.org/wiki/Hungarian_notation), especially _systems Hungarian notation_. + - Naming something `time_in_minutes` (_if it is needed for clarity in your program_) is OK, but not `time_in_minutes_object` (_if you are already using a `time` or `datetime` object_). + - Naming something results is OK, but `results_list` is not good (_especially if there is a chance that the variable could be reassigned as a `tuple` or `set`, or that the function or class name could contradict the return type._) +6. Do not conflict with any [Python keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords) or [Python soft keywords](https://docs.python.org/3/reference/lexical_analysis.html#soft-keywords), These are reserved for special operations in Python and cannot be used as variable names. +7. Do not conflict with/shadow any [Python built in names](https://docs.python.org/3/library/functions.html). diff --git a/lib/common/pylint_data/messages/disallowed-name/good.py b/lib/common/pylint_data/messages/disallowed-name/good.py new file mode 100644 index 0000000..90b8f01 --- /dev/null +++ b/lib/common/pylint_data/messages/disallowed-name/good.py @@ -0,0 +1,6 @@ +def print_fruit(): + print("apples") + +picked_apples = 15 +apples_in_storage = 12 +total_apples = picked_apples + apples_in_storage diff --git a/lib/common/pylint_data/messages/docstring-first-line-empty/bad.py b/lib/common/pylint_data/messages/docstring-first-line-empty/bad.py new file mode 100644 index 0000000..3ec9405 --- /dev/null +++ b/lib/common/pylint_data/messages/docstring-first-line-empty/bad.py @@ -0,0 +1,9 @@ +def foo(): # [docstring-first-line-empty] + """ + Lorem Ipsum is simply dummy text of the printing and typesetting + industry. + + Lorem Ipsum has been the industry's standard dummy text ever since the + 1500s, when an unknown printer took a galley of type and scrambled it + to make a type specimen book + """ diff --git a/lib/common/pylint_data/messages/docstring-first-line-empty/good.py b/lib/common/pylint_data/messages/docstring-first-line-empty/good.py new file mode 100644 index 0000000..16fdd55 --- /dev/null +++ b/lib/common/pylint_data/messages/docstring-first-line-empty/good.py @@ -0,0 +1,8 @@ +def foo(): + """Lorem Ipsum is simply dummy text of the printing and typesetting + industry. + + Lorem Ipsum has been the industry's standard dummy text ever since the + 1500s, when an unknown printer took a galley of type and scrambled it + to make a type specimen book + """ diff --git a/lib/common/pylint_data/messages/duplicate-argument-name/bad.py b/lib/common/pylint_data/messages/duplicate-argument-name/bad.py new file mode 100644 index 0000000..4c9c585 --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-argument-name/bad.py @@ -0,0 +1,2 @@ +def get_fruits(apple, banana, apple): # [duplicate-argument-name] + pass diff --git a/lib/common/pylint_data/messages/duplicate-argument-name/good.py b/lib/common/pylint_data/messages/duplicate-argument-name/good.py new file mode 100644 index 0000000..9fe6d73 --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-argument-name/good.py @@ -0,0 +1,2 @@ +def get_fruits(apple, banana, orange): + pass diff --git a/lib/common/pylint_data/messages/duplicate-bases/bad.py b/lib/common/pylint_data/messages/duplicate-bases/bad.py new file mode 100644 index 0000000..a68e390 --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-bases/bad.py @@ -0,0 +1,6 @@ +class Animal: + pass + + +class Cat(Animal, Animal): # [duplicate-bases] + pass diff --git a/lib/common/pylint_data/messages/duplicate-bases/good.py b/lib/common/pylint_data/messages/duplicate-bases/good.py new file mode 100644 index 0000000..8ed2595 --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-bases/good.py @@ -0,0 +1,10 @@ +class Animal: + pass + + +class Bird(Animal): + pass + + +class Cat(Animal): + pass diff --git a/lib/common/pylint_data/messages/duplicate-code/bad.py b/lib/common/pylint_data/messages/duplicate-code/bad.py new file mode 100644 index 0000000..558794d --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-code/bad.py @@ -0,0 +1,32 @@ +class Apple: + def __init__(self): + self.remaining_bites = 3 + + def take_bite(self): + if self.remaining_bites > 0: + print("You take a bite of the apple.") + self.remaining_bites -= 1 + else: + print("The apple is already eaten up!") + + def eaten_by_animal(self, animal): + self.remaining_bites = 0 + print("The apple has been eaten by an animal.") + + +class Orange: # [duplicate-code] + def __init__(self): + self.remaining_bites = 3 + + def take_bite(self): + if self.remaining_bites > 0: + print("You take a bite of the apple.") + self.remaining_bites -= 1 + else: + print("The orange is already eaten up!") + + def eaten_by_animal(self, animal): + if animal == "cat": + raise ValueError("A cat would never do that !") + self.remaining_bites = 0 + print("The orange has been eaten by an animal.") diff --git a/lib/common/pylint_data/messages/duplicate-code/details.md b/lib/common/pylint_data/messages/duplicate-code/details.md new file mode 100644 index 0000000..ac839cf --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-code/details.md @@ -0,0 +1,10 @@ +If you need to make a change to the logic or functionality of the +duplicated code, you will need to identify all the places that need to +be changed, which can be time-consuming and error-prone. If there are +multiple copies of the same code, then you will also need to test each +copy to ensure that the functionality is correct. Duplicate code can be +confusing for someone who is trying to understand the logic and flow of +the code if they come across multiple identical or nearly identical +blocks of code. The reader can then skim and think something is +identical when it actually isn\'t. This is particularly true during +review. diff --git a/lib/common/pylint_data/messages/duplicate-code/good.py b/lib/common/pylint_data/messages/duplicate-code/good.py new file mode 100644 index 0000000..1da7e13 --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-code/good.py @@ -0,0 +1,27 @@ +class Fruit: + def __init__(self): + self.remaining_bites = 3 + + def take_bite(self): + if self.remaining_bites > 0: + print(f"You take a bite of the {self.__class__.__name__.lower()}.") + self.remaining_bites -= 1 + else: + print(f"The {self.__class__.__name__.lower()} is already eaten up!") + + def eaten_by_animal(self, animal): + self.remaining_bites = 0 + print(f"The {self.__class__.__name__.lower()} has been eaten by an animal.") + + +class Apple(Fruit): ... + + +class Orange(Fruit): + def eaten_by_animal(self, animal): + if animal == "cat": + raise ValueError("A cat would never do that !") + super().eaten_by_animal(animal) + + + diff --git a/lib/common/pylint_data/messages/duplicate-except/bad.py b/lib/common/pylint_data/messages/duplicate-except/bad.py new file mode 100644 index 0000000..e50b423 --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-except/bad.py @@ -0,0 +1,6 @@ +try: + 1 / 0 +except ZeroDivisionError: + pass +except ZeroDivisionError: # [duplicate-except] + pass diff --git a/lib/common/pylint_data/messages/duplicate-except/good.py b/lib/common/pylint_data/messages/duplicate-except/good.py new file mode 100644 index 0000000..b02b365 --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-except/good.py @@ -0,0 +1,4 @@ +try: + 1 / 0 +except ZeroDivisionError: + pass diff --git a/lib/common/pylint_data/messages/duplicate-key/bad.py b/lib/common/pylint_data/messages/duplicate-key/bad.py new file mode 100644 index 0000000..cfd1b59 --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-key/bad.py @@ -0,0 +1 @@ +test_score = {"Mathematics": 85, "Biology": 90, "Mathematics": 75} # [duplicate-key] diff --git a/lib/common/pylint_data/messages/duplicate-key/good.py b/lib/common/pylint_data/messages/duplicate-key/good.py new file mode 100644 index 0000000..75447ce --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-key/good.py @@ -0,0 +1 @@ +test_score = {"Mathematics": 85, "Biology": 90, "History": 75} diff --git a/lib/common/pylint_data/messages/duplicate-key/related.md b/lib/common/pylint_data/messages/duplicate-key/related.md new file mode 100644 index 0000000..985e1a7 --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-key/related.md @@ -0,0 +1,4 @@ +- [Python + Dictionaries](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) +- [Mapping Types --- + dict](https://docs.python.org/3/library/stdtypes.html#typesmapping) diff --git a/lib/common/pylint_data/messages/duplicate-string-formatting-argument/bad.py b/lib/common/pylint_data/messages/duplicate-string-formatting-argument/bad.py new file mode 100644 index 0000000..23d8aae --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-string-formatting-argument/bad.py @@ -0,0 +1,26 @@ +# pylint: disable=missing-docstring, consider-using-f-string + +SEE = "see 👀" +SEA = "sea 🌊" + +# +1: [duplicate-string-formatting-argument,duplicate-string-formatting-argument] +CONST = """ +A sailor went to {}, {}, {} +To {} what he could {}, {}, {} +But all that he could {}, {}, {} +Was the bottom of the deep blue {}, {}, {}! +""".format( + SEA, + SEA, + SEA, + SEE, + SEE, + SEE, + SEE, + SEE, + SEE, + SEE, + SEA, + SEA, + SEA, +) diff --git a/lib/common/pylint_data/messages/duplicate-string-formatting-argument/good.py b/lib/common/pylint_data/messages/duplicate-string-formatting-argument/good.py new file mode 100644 index 0000000..2aef21e --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-string-formatting-argument/good.py @@ -0,0 +1,11 @@ +# pylint: disable=missing-docstring, consider-using-f-string + +SEE = "see 👀" +SEA = "sea 🌊" + +CONST = """ +A sailor went to {sea}, {sea}, {sea} +To {see} what he could {see}, {see}, {see} +But all that he could {see}, {see}, {see} +Was the bottom of the deep blue {sea}, {sea}, {sea}! +""".format(sea=SEA, see=SEE) diff --git a/lib/common/pylint_data/messages/duplicate-value/bad.py b/lib/common/pylint_data/messages/duplicate-value/bad.py new file mode 100644 index 0000000..975eebd --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-value/bad.py @@ -0,0 +1 @@ +incorrect_set = {"value1", 23, 5, "value1"} # [duplicate-value] diff --git a/lib/common/pylint_data/messages/duplicate-value/good.py b/lib/common/pylint_data/messages/duplicate-value/good.py new file mode 100644 index 0000000..beeeb39 --- /dev/null +++ b/lib/common/pylint_data/messages/duplicate-value/good.py @@ -0,0 +1 @@ +correct_set = {"value1", 23, 5} diff --git a/lib/common/pylint_data/messages/else-if-used/bad.py b/lib/common/pylint_data/messages/else-if-used/bad.py new file mode 100644 index 0000000..55cf422 --- /dev/null +++ b/lib/common/pylint_data/messages/else-if-used/bad.py @@ -0,0 +1,7 @@ +if input(): + pass +else: + if len(input()) >= 10: # [else-if-used] + pass + else: + pass diff --git a/lib/common/pylint_data/messages/else-if-used/good.py b/lib/common/pylint_data/messages/else-if-used/good.py new file mode 100644 index 0000000..39eb3ba --- /dev/null +++ b/lib/common/pylint_data/messages/else-if-used/good.py @@ -0,0 +1,6 @@ +if input(): + pass +elif len(input()) >= 10: + pass +else: + pass diff --git a/lib/common/pylint_data/messages/empty-comment/bad.py b/lib/common/pylint_data/messages/empty-comment/bad.py new file mode 100644 index 0000000..f9d7f8e --- /dev/null +++ b/lib/common/pylint_data/messages/empty-comment/bad.py @@ -0,0 +1,5 @@ +# +1:[empty-comment] +# + +# +1:[empty-comment] +x = 0 # diff --git a/lib/common/pylint_data/messages/empty-comment/good.py b/lib/common/pylint_data/messages/empty-comment/good.py new file mode 100644 index 0000000..f05c63d --- /dev/null +++ b/lib/common/pylint_data/messages/empty-comment/good.py @@ -0,0 +1,3 @@ +# comment + +x = 0 # comment diff --git a/lib/common/pylint_data/messages/empty-docstring/bad.py b/lib/common/pylint_data/messages/empty-docstring/bad.py new file mode 100644 index 0000000..33dbb5f --- /dev/null +++ b/lib/common/pylint_data/messages/empty-docstring/bad.py @@ -0,0 +1,2 @@ +def foo(): # [empty-docstring] + """""" diff --git a/lib/common/pylint_data/messages/empty-docstring/good.py b/lib/common/pylint_data/messages/empty-docstring/good.py new file mode 100644 index 0000000..b587ca4 --- /dev/null +++ b/lib/common/pylint_data/messages/empty-docstring/good.py @@ -0,0 +1,2 @@ +def foo(): + """A dummy description.""" diff --git a/lib/common/pylint_data/messages/eq-without-hash/bad.py b/lib/common/pylint_data/messages/eq-without-hash/bad.py new file mode 100644 index 0000000..42df9e8 --- /dev/null +++ b/lib/common/pylint_data/messages/eq-without-hash/bad.py @@ -0,0 +1,6 @@ +class Fruit: # [eq-without-hash] + def __init__(self) -> None: + self.name = "apple" + + def __eq__(self, other: object) -> bool: + return isinstance(other, Fruit) and other.name == self.name diff --git a/lib/common/pylint_data/messages/eq-without-hash/good.py b/lib/common/pylint_data/messages/eq-without-hash/good.py new file mode 100644 index 0000000..bf62450 --- /dev/null +++ b/lib/common/pylint_data/messages/eq-without-hash/good.py @@ -0,0 +1,9 @@ +class Fruit: + def __init__(self) -> None: + self.name = "apple" + + def __eq__(self, other: object) -> bool: + return isinstance(other, Fruit) and other.name == self.name + + def __hash__(self) -> int: + return hash(self.name) diff --git a/lib/common/pylint_data/messages/eval-used/bad.py b/lib/common/pylint_data/messages/eval-used/bad.py new file mode 100644 index 0000000..db26f17 --- /dev/null +++ b/lib/common/pylint_data/messages/eval-used/bad.py @@ -0,0 +1 @@ +eval("[1, 2, 3]") # [eval-used] diff --git a/lib/common/pylint_data/messages/eval-used/good.py b/lib/common/pylint_data/messages/eval-used/good.py new file mode 100644 index 0000000..094ecae --- /dev/null +++ b/lib/common/pylint_data/messages/eval-used/good.py @@ -0,0 +1,3 @@ +from ast import literal_eval + +literal_eval("[1, 2, 3]") diff --git a/lib/common/pylint_data/messages/exec-used/bad.py b/lib/common/pylint_data/messages/exec-used/bad.py new file mode 100644 index 0000000..72514e3 --- /dev/null +++ b/lib/common/pylint_data/messages/exec-used/bad.py @@ -0,0 +1,4 @@ +username = "Ada" +code_to_execute = f"""input('Enter code to be executed please, {username}: ')""" +program = exec(code_to_execute) # [exec-used] +exec(program) # [exec-used] diff --git a/lib/common/pylint_data/messages/exec-used/details.md b/lib/common/pylint_data/messages/exec-used/details.md new file mode 100644 index 0000000..91b74c6 --- /dev/null +++ b/lib/common/pylint_data/messages/exec-used/details.md @@ -0,0 +1,54 @@ +The available methods and variables used in `exec()` may introduce a +security hole. You can restrict the use of these variables and methods +by passing optional globals and locals parameters (dictionaries) to the +`exec()` method. + +However, use of `exec()` is still insecure if you allow some functions +like `__import__` or `open`. For example, consider the following call +that writes a file to the user\'s system and then execute code +unrestrained by the `allowed_globals`, or `allowed_locals` parameters: + +``` python +import textwrap + + +def forbid_print(*args): + raise ValueError("This is raised when a print is used") + + +allowed_globals = { + "__builtins__": { + "__import__": __builtins__.__import__, + "open": __builtins__.open, + "print": forbid_print, + } +} + +exec( + textwrap.dedent( + """ + import textwrap + + with open("nefarious.py", "w") as f: + f.write(textwrap.dedent(''' + def connive(): + print("Here's some code as nefarious as imaginable") + ''')) + + import nefarious + + nefarious.connive() # This will NOT raise a ValueError + """ + ), + allowed_globals, +) +``` + +The import is used only for readability of the example in this case but +it could import a dangerous functions: + +- `subprocess.run('echo "print(\"Hello, World!\")" > nefarious.py'` +- `pathlib.Path("nefarious.py").write_file("print(\"Hello, World!\")")` +- `os.system('echo "print(\"Hello, World!\")" > nefarious.py')` +- `logging.basicConfig(filename='nefarious.py'); logging.error('print("Hello, World!")')` +- etc. diff --git a/lib/common/pylint_data/messages/exec-used/good.py b/lib/common/pylint_data/messages/exec-used/good.py new file mode 100644 index 0000000..fe247d5 --- /dev/null +++ b/lib/common/pylint_data/messages/exec-used/good.py @@ -0,0 +1,11 @@ +def get_user_code(name): + return input(f"Enter code to be executed please, {name}: ") + + +username = "Ada" +# If the globals dictionary does not contain a value for the key __builtins__, +# all builtins are allowed. You need to be explicit about it being disallowed. +allowed_globals = {"__builtins__": {}} +allowed_locals = {} +# pylint: disable-next=exec-used +exec(get_user_code(username), allowed_globals, allowed_locals) diff --git a/lib/common/pylint_data/messages/exec-used/related.md b/lib/common/pylint_data/messages/exec-used/related.md new file mode 100644 index 0000000..2f9a7b7 --- /dev/null +++ b/lib/common/pylint_data/messages/exec-used/related.md @@ -0,0 +1,4 @@ +- [Be careful with exec and eval in + Python](https://lucumr.pocoo.org/2011/2/1/exec-in-python/) +- [Python + documentation](https://docs.python.org/3/library/functions.html#exec) diff --git a/lib/common/pylint_data/messages/expression-not-assigned/bad.py b/lib/common/pylint_data/messages/expression-not-assigned/bad.py new file mode 100644 index 0000000..fa696d4 --- /dev/null +++ b/lib/common/pylint_data/messages/expression-not-assigned/bad.py @@ -0,0 +1 @@ +str(42) == "42" # [expression-not-assigned] diff --git a/lib/common/pylint_data/messages/expression-not-assigned/good.py b/lib/common/pylint_data/messages/expression-not-assigned/good.py new file mode 100644 index 0000000..fda04a8 --- /dev/null +++ b/lib/common/pylint_data/messages/expression-not-assigned/good.py @@ -0,0 +1 @@ +are_equal: bool = str(42) == "42" diff --git a/lib/common/pylint_data/messages/f-string-without-interpolation/bad.py b/lib/common/pylint_data/messages/f-string-without-interpolation/bad.py new file mode 100644 index 0000000..a317798 --- /dev/null +++ b/lib/common/pylint_data/messages/f-string-without-interpolation/bad.py @@ -0,0 +1,3 @@ +x = 1 +y = 2 +print(f"x + y = x + y") # [f-string-without-interpolation] diff --git a/lib/common/pylint_data/messages/f-string-without-interpolation/good.py b/lib/common/pylint_data/messages/f-string-without-interpolation/good.py new file mode 100644 index 0000000..9af2f97 --- /dev/null +++ b/lib/common/pylint_data/messages/f-string-without-interpolation/good.py @@ -0,0 +1,3 @@ +x = 1 +y = 2 +print(f"{x} + {y} = {x + y}") diff --git a/lib/common/pylint_data/messages/fatal/details.md b/lib/common/pylint_data/messages/fatal/details.md new file mode 100644 index 0000000..0ef4d8c --- /dev/null +++ b/lib/common/pylint_data/messages/fatal/details.md @@ -0,0 +1,2 @@ +This is a message linked to an internal problem in pylint. There\'s +nothing to change in your code. diff --git a/lib/common/pylint_data/messages/file-ignored/bad.py b/lib/common/pylint_data/messages/file-ignored/bad.py new file mode 100644 index 0000000..ce04564 --- /dev/null +++ b/lib/common/pylint_data/messages/file-ignored/bad.py @@ -0,0 +1,2 @@ +# pylint: skip-file +# -1: [file-ignored] diff --git a/lib/common/pylint_data/messages/file-ignored/details.md b/lib/common/pylint_data/messages/file-ignored/details.md new file mode 100644 index 0000000..62b105b --- /dev/null +++ b/lib/common/pylint_data/messages/file-ignored/details.md @@ -0,0 +1,2 @@ +There\'s no checks at all for a file if it starts by +`# pylint: skip-file`. diff --git a/lib/common/pylint_data/messages/file-ignored/good.py b/lib/common/pylint_data/messages/file-ignored/good.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/common/pylint_data/messages/fixme/bad.py b/lib/common/pylint_data/messages/fixme/bad.py new file mode 100644 index 0000000..47955d7 --- /dev/null +++ b/lib/common/pylint_data/messages/fixme/bad.py @@ -0,0 +1 @@ +# TODO: We should fix this at some point # [fixme] diff --git a/lib/common/pylint_data/messages/fixme/details.md b/lib/common/pylint_data/messages/fixme/details.md new file mode 100644 index 0000000..a123591 --- /dev/null +++ b/lib/common/pylint_data/messages/fixme/details.md @@ -0,0 +1,4 @@ +You can use regular expressions and the `notes-rgx` option to create +some constraints for this message. See [the following +issue](https://github.com/pylint-dev/pylint/issues/2874) for some +examples. diff --git a/lib/common/pylint_data/messages/fixme/good.py b/lib/common/pylint_data/messages/fixme/good.py new file mode 100644 index 0000000..55cfc22 --- /dev/null +++ b/lib/common/pylint_data/messages/fixme/good.py @@ -0,0 +1,9 @@ +# The issue was added to the bug tracker: no longer need the comment + +# or + +# The issue was fixed: no longer need the comment + +# or + +# We no longer want to fix this: no longer need the comment diff --git a/lib/common/pylint_data/messages/forgotten-debug-statement/bad.py b/lib/common/pylint_data/messages/forgotten-debug-statement/bad.py new file mode 100644 index 0000000..e371853 --- /dev/null +++ b/lib/common/pylint_data/messages/forgotten-debug-statement/bad.py @@ -0,0 +1,17 @@ +import pdb + + +def find_the_treasure(clues): + for clue in clues: + pdb.set_trace() # [forgotten-debug-statement] + if "treasure" in clue: + return True + return False + + +treasure_hunt = [ + "Dead Man's Chest", + "X marks the spot", + "The treasure is buried near the palm tree", +] +find_the_treasure(treasure_hunt) diff --git a/lib/common/pylint_data/messages/forgotten-debug-statement/good.py b/lib/common/pylint_data/messages/forgotten-debug-statement/good.py new file mode 100644 index 0000000..7896ff5 --- /dev/null +++ b/lib/common/pylint_data/messages/forgotten-debug-statement/good.py @@ -0,0 +1,13 @@ +def find_the_treasure(clues): + for clue in clues: + if "treasure" in clue: + return True + return False + + +treasure_hunt = [ + "Dead Man's Chest", + "X marks the spot", + "The treasure is buried near the palm tree", +] +find_the_treasure(treasure_hunt) diff --git a/lib/common/pylint_data/messages/format-combined-specification/bad.py b/lib/common/pylint_data/messages/format-combined-specification/bad.py new file mode 100644 index 0000000..f530aa0 --- /dev/null +++ b/lib/common/pylint_data/messages/format-combined-specification/bad.py @@ -0,0 +1 @@ +print("{} {1}".format("hello", "world")) # [format-combined-specification] diff --git a/lib/common/pylint_data/messages/format-combined-specification/good.py b/lib/common/pylint_data/messages/format-combined-specification/good.py new file mode 100644 index 0000000..a4872c8 --- /dev/null +++ b/lib/common/pylint_data/messages/format-combined-specification/good.py @@ -0,0 +1,6 @@ +# Index formatting +print("{0} {1}".format("hello", "world")) + + +# Order formatting +print("{} {}".format("hello", "world")) diff --git a/lib/common/pylint_data/messages/format-needs-mapping/bad.py b/lib/common/pylint_data/messages/format-needs-mapping/bad.py new file mode 100644 index 0000000..b265ccd --- /dev/null +++ b/lib/common/pylint_data/messages/format-needs-mapping/bad.py @@ -0,0 +1 @@ +print("%(x)d %(y)d" % [1, 2]) # [format-needs-mapping] diff --git a/lib/common/pylint_data/messages/format-needs-mapping/good.py b/lib/common/pylint_data/messages/format-needs-mapping/good.py new file mode 100644 index 0000000..bed0122 --- /dev/null +++ b/lib/common/pylint_data/messages/format-needs-mapping/good.py @@ -0,0 +1 @@ +print("%(x)d %(y)d" % {"x": 1, "y": 2}) diff --git a/lib/common/pylint_data/messages/format-string-without-interpolation/bad.py b/lib/common/pylint_data/messages/format-string-without-interpolation/bad.py new file mode 100644 index 0000000..a5ff640 --- /dev/null +++ b/lib/common/pylint_data/messages/format-string-without-interpolation/bad.py @@ -0,0 +1 @@ +print("number".format(1)) # [format-string-without-interpolation] diff --git a/lib/common/pylint_data/messages/format-string-without-interpolation/good.py b/lib/common/pylint_data/messages/format-string-without-interpolation/good.py new file mode 100644 index 0000000..097f796 --- /dev/null +++ b/lib/common/pylint_data/messages/format-string-without-interpolation/good.py @@ -0,0 +1 @@ +print("number: {}".format(1)) diff --git a/lib/common/pylint_data/messages/function-redefined/bad.py b/lib/common/pylint_data/messages/function-redefined/bad.py new file mode 100644 index 0000000..0e3c308 --- /dev/null +++ b/lib/common/pylint_data/messages/function-redefined/bad.py @@ -0,0 +1,6 @@ +def get_email(): + pass + + +def get_email(): # [function-redefined] + pass diff --git a/lib/common/pylint_data/messages/function-redefined/good.py b/lib/common/pylint_data/messages/function-redefined/good.py new file mode 100644 index 0000000..96efb4b --- /dev/null +++ b/lib/common/pylint_data/messages/function-redefined/good.py @@ -0,0 +1,2 @@ +def get_email(): + pass diff --git a/lib/common/pylint_data/messages/global-at-module-level/bad.py b/lib/common/pylint_data/messages/global-at-module-level/bad.py new file mode 100644 index 0000000..e38da25 --- /dev/null +++ b/lib/common/pylint_data/messages/global-at-module-level/bad.py @@ -0,0 +1,2 @@ +price = 25 +global price # [global-at-module-level] diff --git a/lib/common/pylint_data/messages/global-at-module-level/good.py b/lib/common/pylint_data/messages/global-at-module-level/good.py new file mode 100644 index 0000000..4f37296 --- /dev/null +++ b/lib/common/pylint_data/messages/global-at-module-level/good.py @@ -0,0 +1 @@ +price = 25 diff --git a/lib/common/pylint_data/messages/global-at-module-level/related.md b/lib/common/pylint_data/messages/global-at-module-level/related.md new file mode 100644 index 0000000..fd9f14c --- /dev/null +++ b/lib/common/pylint_data/messages/global-at-module-level/related.md @@ -0,0 +1,6 @@ +- [Official Python FAQ - global and + local](https://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python) +- [PEP 3104 - Access to Names in Outer + Scopes](https://peps.python.org/pep-3104/) +- [Python global + statement](https://docs.python.org/3/reference/simple_stmts.html#global) diff --git a/lib/common/pylint_data/messages/global-statement/bad.py b/lib/common/pylint_data/messages/global-statement/bad.py new file mode 100644 index 0000000..aca1dab --- /dev/null +++ b/lib/common/pylint_data/messages/global-statement/bad.py @@ -0,0 +1,11 @@ +var = 1 + + +def foo(): + global var # [global-statement] + var = 10 + print(var) + + +foo() +print(var) diff --git a/lib/common/pylint_data/messages/global-statement/good.py b/lib/common/pylint_data/messages/global-statement/good.py new file mode 100644 index 0000000..34958b5 --- /dev/null +++ b/lib/common/pylint_data/messages/global-statement/good.py @@ -0,0 +1,10 @@ +var = 1 + + +def foo(): + print(var) + return 10 + + +var = foo() +print(var) diff --git a/lib/common/pylint_data/messages/global-variable-not-assigned/bad.py b/lib/common/pylint_data/messages/global-variable-not-assigned/bad.py new file mode 100644 index 0000000..d1d9075 --- /dev/null +++ b/lib/common/pylint_data/messages/global-variable-not-assigned/bad.py @@ -0,0 +1,6 @@ +TOMATO = "black cherry" + + +def update_tomato(): + global TOMATO # [global-variable-not-assigned] + print(TOMATO) diff --git a/lib/common/pylint_data/messages/global-variable-not-assigned/good.py b/lib/common/pylint_data/messages/global-variable-not-assigned/good.py new file mode 100644 index 0000000..0736bb4 --- /dev/null +++ b/lib/common/pylint_data/messages/global-variable-not-assigned/good.py @@ -0,0 +1,6 @@ +TOMATO = "black cherry" + + +def update_tomato(): + global TOMATO + TOMATO = "moneymaker" diff --git a/lib/common/pylint_data/messages/global-variable-undefined/bad.py b/lib/common/pylint_data/messages/global-variable-undefined/bad.py new file mode 100644 index 0000000..44e4f3a --- /dev/null +++ b/lib/common/pylint_data/messages/global-variable-undefined/bad.py @@ -0,0 +1,3 @@ +def update_tomato(): + global TOMATO # [global-variable-undefined] + TOMATO = "moneymaker" diff --git a/lib/common/pylint_data/messages/global-variable-undefined/good.py b/lib/common/pylint_data/messages/global-variable-undefined/good.py new file mode 100644 index 0000000..0736bb4 --- /dev/null +++ b/lib/common/pylint_data/messages/global-variable-undefined/good.py @@ -0,0 +1,6 @@ +TOMATO = "black cherry" + + +def update_tomato(): + global TOMATO + TOMATO = "moneymaker" diff --git a/lib/common/pylint_data/messages/implicit-flag-alias/bad.py b/lib/common/pylint_data/messages/implicit-flag-alias/bad.py new file mode 100644 index 0000000..1d12e4f --- /dev/null +++ b/lib/common/pylint_data/messages/implicit-flag-alias/bad.py @@ -0,0 +1,7 @@ +from enum import IntFlag + + +class FilePermissions(IntFlag): + READ = 1 + WRITE = 2 + EXECUTE = 3 # [implicit-flag-alias] diff --git a/lib/common/pylint_data/messages/implicit-flag-alias/good.py b/lib/common/pylint_data/messages/implicit-flag-alias/good.py new file mode 100644 index 0000000..78847d1 --- /dev/null +++ b/lib/common/pylint_data/messages/implicit-flag-alias/good.py @@ -0,0 +1,7 @@ +from enum import IntFlag + + +class FilePermissions(IntFlag): + READ = 1 + WRITE = 2 + EXECUTE = 4 diff --git a/lib/common/pylint_data/messages/implicit-str-concat/bad.py b/lib/common/pylint_data/messages/implicit-str-concat/bad.py new file mode 100644 index 0000000..5febda6 --- /dev/null +++ b/lib/common/pylint_data/messages/implicit-str-concat/bad.py @@ -0,0 +1,6 @@ +# Declaring a list literal without a comma between elements. +x = ["a" "b"] # [implicit-str-concat] + +# Using a context handler without a comma separating the arguments. +with open("hello.txt" "r") as f: # [implicit-str-concat] + print(f.read()) diff --git a/lib/common/pylint_data/messages/implicit-str-concat/details.md b/lib/common/pylint_data/messages/implicit-str-concat/details.md new file mode 100644 index 0000000..e1375a1 --- /dev/null +++ b/lib/common/pylint_data/messages/implicit-str-concat/details.md @@ -0,0 +1,42 @@ +By default, detection of implicit string concatenation of line jumps is +disabled. Hence the following code will not trigger this rule: + +``` python +SEQ = ('a', 'b' + 'c') +``` + +In order to detect this case, you must enable +\`check-str-concat-over-line-jumps\`: + +``` toml +[STRING_CONSTANT] +check-str-concat-over-line-jumps = true +``` + +However, the drawback of this setting is that it will trigger false +positive for string parameters passed on multiple lines in function +calls: + +``` python +warnings.warn( + "rotate() is deprecated and will be removed in a future release. " + "Use the rotation() context manager instead.", + DeprecationWarning, + stacklevel=3, +) +``` + +No message will be emitted, though, if you clarify the wanted +concatenation with parentheses: + +``` python +warnings.warn( + ( + "rotate() is deprecated and will be removed in a future release. " + "Use the rotation() context manager instead." + ), + DeprecationWarning, + stacklevel=3, +) +``` diff --git a/lib/common/pylint_data/messages/implicit-str-concat/good.py b/lib/common/pylint_data/messages/implicit-str-concat/good.py new file mode 100644 index 0000000..ebf50c7 --- /dev/null +++ b/lib/common/pylint_data/messages/implicit-str-concat/good.py @@ -0,0 +1,4 @@ +x = ["a", "b"] + +with open("hello.txt", "r") as f: + print(f.read()) diff --git a/lib/common/pylint_data/messages/import-error/bad.py b/lib/common/pylint_data/messages/import-error/bad.py new file mode 100644 index 0000000..672564c --- /dev/null +++ b/lib/common/pylint_data/messages/import-error/bad.py @@ -0,0 +1 @@ +from patlib import Path # [import-error] diff --git a/lib/common/pylint_data/messages/import-error/details.md b/lib/common/pylint_data/messages/import-error/details.md new file mode 100644 index 0000000..431cca7 --- /dev/null +++ b/lib/common/pylint_data/messages/import-error/details.md @@ -0,0 +1,5 @@ +This can happen if you\'re importing a package that is not installed in +your environment, or if you made a typo. + +The solution is to install the package via pip/setup.py/wheel or fix the +typo. diff --git a/lib/common/pylint_data/messages/import-error/good.py b/lib/common/pylint_data/messages/import-error/good.py new file mode 100644 index 0000000..2bb88df --- /dev/null +++ b/lib/common/pylint_data/messages/import-error/good.py @@ -0,0 +1 @@ +from pathlib import Path diff --git a/lib/common/pylint_data/messages/import-outside-toplevel/bad.py b/lib/common/pylint_data/messages/import-outside-toplevel/bad.py new file mode 100644 index 0000000..5262ba7 --- /dev/null +++ b/lib/common/pylint_data/messages/import-outside-toplevel/bad.py @@ -0,0 +1,4 @@ +def print_python_version(): + import sys # [import-outside-toplevel] + + print(sys.version_info) diff --git a/lib/common/pylint_data/messages/import-outside-toplevel/good.py b/lib/common/pylint_data/messages/import-outside-toplevel/good.py new file mode 100644 index 0000000..d77c91a --- /dev/null +++ b/lib/common/pylint_data/messages/import-outside-toplevel/good.py @@ -0,0 +1,5 @@ +import sys + + +def print_python_version(): + print(sys.version_info) diff --git a/lib/common/pylint_data/messages/import-private-name/bad.py b/lib/common/pylint_data/messages/import-private-name/bad.py new file mode 100644 index 0000000..e562e49 --- /dev/null +++ b/lib/common/pylint_data/messages/import-private-name/bad.py @@ -0,0 +1,8 @@ +from argparse import _AttributeHolder, _SubParsersAction # [import-private-name] + +attr_holder = _AttributeHolder() + + +def add_sub_parser(sub_parsers: _SubParsersAction): + sub_parsers.add_parser("my_subparser") + # ... diff --git a/lib/common/pylint_data/messages/import-private-name/details.md b/lib/common/pylint_data/messages/import-private-name/details.md new file mode 100644 index 0000000..eb0afe5 --- /dev/null +++ b/lib/common/pylint_data/messages/import-private-name/details.md @@ -0,0 +1,2 @@ +Using private imports expose you to unexpected breaking changes for any +version bump of your dependencies, even in patch versions. diff --git a/lib/common/pylint_data/messages/import-private-name/good.py b/lib/common/pylint_data/messages/import-private-name/good.py new file mode 100644 index 0000000..810005f --- /dev/null +++ b/lib/common/pylint_data/messages/import-private-name/good.py @@ -0,0 +1,8 @@ +"""Private import can be used as type annotations.""" + +from argparse import _SubParsersAction + + +def add_sub_parser(sub_parsers: _SubParsersAction): + sub_parsers.add_parser("my_subparser") + # ... diff --git a/lib/common/pylint_data/messages/import-self/details.md b/lib/common/pylint_data/messages/import-self/details.md new file mode 100644 index 0000000..13c8082 --- /dev/null +++ b/lib/common/pylint_data/messages/import-self/details.md @@ -0,0 +1,12 @@ +Say you have a file called `my_file.py`. `import-self` would be raised +on the following code: + + from my_file import a_function # [import-self] + + def a_function(): + pass + +The solution would be to remove the import: + + def a_function(): + pass diff --git a/lib/common/pylint_data/messages/inconsistent-mro/bad.py b/lib/common/pylint_data/messages/inconsistent-mro/bad.py new file mode 100644 index 0000000..93f9aa5 --- /dev/null +++ b/lib/common/pylint_data/messages/inconsistent-mro/bad.py @@ -0,0 +1,10 @@ +class A: + pass + + +class B(A): + pass + + +class C(A, B): # [inconsistent-mro] + pass diff --git a/lib/common/pylint_data/messages/inconsistent-mro/good.py b/lib/common/pylint_data/messages/inconsistent-mro/good.py new file mode 100644 index 0000000..5fccd63 --- /dev/null +++ b/lib/common/pylint_data/messages/inconsistent-mro/good.py @@ -0,0 +1,10 @@ +class A: + pass + + +class B(A): + pass + + +class C(B): # or 'B, A' or 'A' but not 'A, B' + pass diff --git a/lib/common/pylint_data/messages/inconsistent-quotes/bad.py b/lib/common/pylint_data/messages/inconsistent-quotes/bad.py new file mode 100644 index 0000000..2a18366 --- /dev/null +++ b/lib/common/pylint_data/messages/inconsistent-quotes/bad.py @@ -0,0 +1,3 @@ +import datetime + +print('Current year: ', datetime.date.today().strftime("%Y")) # [inconsistent-quotes] diff --git a/lib/common/pylint_data/messages/inconsistent-quotes/good.py b/lib/common/pylint_data/messages/inconsistent-quotes/good.py new file mode 100644 index 0000000..87431bc --- /dev/null +++ b/lib/common/pylint_data/messages/inconsistent-quotes/good.py @@ -0,0 +1,3 @@ +import datetime + +print("Current year: ", datetime.date.today().strftime("%Y")) diff --git a/lib/common/pylint_data/messages/inconsistent-return-statements/bad.py b/lib/common/pylint_data/messages/inconsistent-return-statements/bad.py new file mode 100644 index 0000000..32bf948 --- /dev/null +++ b/lib/common/pylint_data/messages/inconsistent-return-statements/bad.py @@ -0,0 +1,3 @@ +def get_the_answer(value: str) -> str | None: # [inconsistent-return-statements] + if value: + return value diff --git a/lib/common/pylint_data/messages/inconsistent-return-statements/good.py b/lib/common/pylint_data/messages/inconsistent-return-statements/good.py new file mode 100644 index 0000000..991527b --- /dev/null +++ b/lib/common/pylint_data/messages/inconsistent-return-statements/good.py @@ -0,0 +1,4 @@ +def get_the_answer(value: str) -> str | None: + if value: + return value + return None diff --git a/lib/common/pylint_data/messages/inherit-non-class/bad.py b/lib/common/pylint_data/messages/inherit-non-class/bad.py new file mode 100644 index 0000000..4a7926c --- /dev/null +++ b/lib/common/pylint_data/messages/inherit-non-class/bad.py @@ -0,0 +1,2 @@ +class Fruit(bool): # [inherit-non-class] + pass diff --git a/lib/common/pylint_data/messages/inherit-non-class/good.py b/lib/common/pylint_data/messages/inherit-non-class/good.py new file mode 100644 index 0000000..dc48bd0 --- /dev/null +++ b/lib/common/pylint_data/messages/inherit-non-class/good.py @@ -0,0 +1,3 @@ +class Fruit: + def __bool__(self): + pass diff --git a/lib/common/pylint_data/messages/init-is-generator/bad.py b/lib/common/pylint_data/messages/init-is-generator/bad.py new file mode 100644 index 0000000..415b94c --- /dev/null +++ b/lib/common/pylint_data/messages/init-is-generator/bad.py @@ -0,0 +1,6 @@ +class Fruit: + def __init__(self, worms): # [init-is-generator] + yield from worms + + +apple = Fruit(["Fahad", "Anisha", "Tabatha"]) diff --git a/lib/common/pylint_data/messages/init-is-generator/good.py b/lib/common/pylint_data/messages/init-is-generator/good.py new file mode 100644 index 0000000..1f5bf00 --- /dev/null +++ b/lib/common/pylint_data/messages/init-is-generator/good.py @@ -0,0 +1,11 @@ +class Fruit: + def __init__(self, worms): + self.__worms = worms + + def worms(self): + yield from self.__worms + + +apple = Fruit(["Fahad", "Anisha", "Tabatha"]) +for worm in apple.worms(): + pass diff --git a/lib/common/pylint_data/messages/invalid-all-format/bad.py b/lib/common/pylint_data/messages/invalid-all-format/bad.py new file mode 100644 index 0000000..c891377 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-all-format/bad.py @@ -0,0 +1,3 @@ +__all__ = "CONST" # [invalid-all-format] + +CONST = 42 diff --git a/lib/common/pylint_data/messages/invalid-all-format/good.py b/lib/common/pylint_data/messages/invalid-all-format/good.py new file mode 100644 index 0000000..bb64b60 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-all-format/good.py @@ -0,0 +1,3 @@ +__all__ = ("CONST",) + +CONST = 42 diff --git a/lib/common/pylint_data/messages/invalid-all-object/bad.py b/lib/common/pylint_data/messages/invalid-all-object/bad.py new file mode 100644 index 0000000..0450972 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-all-object/bad.py @@ -0,0 +1,13 @@ +__all__ = ( + None, # [invalid-all-object] + Fruit, + Worm, +) + + +class Fruit: + pass + + +class Worm: + pass diff --git a/lib/common/pylint_data/messages/invalid-all-object/details.md b/lib/common/pylint_data/messages/invalid-all-object/details.md new file mode 100644 index 0000000..de24429 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-all-object/details.md @@ -0,0 +1,6 @@ +From [The Python Language Reference -- The import statement](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement): + +: \"The [public names]{.title-ref} defined by a module are determined + by checking the module\'s namespace for a variable named `__all__`; + if defined, it must be a sequence of strings which are names defined + or imported by that module.\" diff --git a/lib/common/pylint_data/messages/invalid-all-object/good.py b/lib/common/pylint_data/messages/invalid-all-object/good.py new file mode 100644 index 0000000..351cfa1 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-all-object/good.py @@ -0,0 +1,9 @@ +__all__ = ["Fruit", "Worm"] + + +class Fruit: + pass + + +class Worm: + pass diff --git a/lib/common/pylint_data/messages/invalid-all-object/related.md b/lib/common/pylint_data/messages/invalid-all-object/related.md new file mode 100644 index 0000000..574057d --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-all-object/related.md @@ -0,0 +1,2 @@ +- [PEP 8 -- Style Guide for Python + Code](https://peps.python.org/pep-0008/#module-level-dunder-names) diff --git a/lib/common/pylint_data/messages/invalid-bool-returned/bad.py b/lib/common/pylint_data/messages/invalid-bool-returned/bad.py new file mode 100644 index 0000000..74c0000 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-bool-returned/bad.py @@ -0,0 +1,5 @@ +class CustomBool: + """__bool__ returns an int""" + + def __bool__(self): # [invalid-bool-returned] + return 1 diff --git a/lib/common/pylint_data/messages/invalid-bool-returned/good.py b/lib/common/pylint_data/messages/invalid-bool-returned/good.py new file mode 100644 index 0000000..9bca165 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-bool-returned/good.py @@ -0,0 +1,5 @@ +class CustomBool: + """__bool__ returns `bool`""" + + def __bool__(self): + return True diff --git a/lib/common/pylint_data/messages/invalid-bytes-returned/bad.py b/lib/common/pylint_data/messages/invalid-bytes-returned/bad.py new file mode 100644 index 0000000..e20e590 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-bytes-returned/bad.py @@ -0,0 +1,5 @@ +class CustomBytes: + """__bytes__ returns """ + + def __bytes__(self): # [invalid-bytes-returned] + return "123" diff --git a/lib/common/pylint_data/messages/invalid-bytes-returned/good.py b/lib/common/pylint_data/messages/invalid-bytes-returned/good.py new file mode 100644 index 0000000..548303d --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-bytes-returned/good.py @@ -0,0 +1,5 @@ +class CustomBytes: + """__bytes__ returns """ + + def __bytes__(self): + return b"some bytes" diff --git a/lib/common/pylint_data/messages/invalid-character-backspace/bad.py b/lib/common/pylint_data/messages/invalid-character-backspace/bad.py new file mode 100644 index 0000000..ac9ed67 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-backspace/bad.py @@ -0,0 +1 @@ +STRING = "Invalid character backspace " # [invalid-character-backspace] diff --git a/lib/common/pylint_data/messages/invalid-character-backspace/good.py b/lib/common/pylint_data/messages/invalid-character-backspace/good.py new file mode 100644 index 0000000..3381c5f --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-backspace/good.py @@ -0,0 +1 @@ +STRING = "Valid character backspace \b" diff --git a/lib/common/pylint_data/messages/invalid-character-carriage-return/details.md b/lib/common/pylint_data/messages/invalid-character-carriage-return/details.md new file mode 100644 index 0000000..9537ae2 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-carriage-return/details.md @@ -0,0 +1,7 @@ +This message exists because one of our checkers is very generic, but +it\'s never going to raise during normal use as it\'s a `syntax-error` +that would prevent the python ast (and thus pylint) from constructing a +code representation of the file. + +You could encounter it by feeding a properly constructed node directly +to the checker. diff --git a/lib/common/pylint_data/messages/invalid-character-carriage-return/good.py b/lib/common/pylint_data/messages/invalid-character-carriage-return/good.py new file mode 100644 index 0000000..ae08661 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-carriage-return/good.py @@ -0,0 +1 @@ +STRING = "Valid carriage return: \r" diff --git a/lib/common/pylint_data/messages/invalid-character-esc/bad.py b/lib/common/pylint_data/messages/invalid-character-esc/bad.py new file mode 100644 index 0000000..0d50cab --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-esc/bad.py @@ -0,0 +1 @@ +STRING = "Invalid escape character " # [invalid-character-esc] diff --git a/lib/common/pylint_data/messages/invalid-character-esc/good.py b/lib/common/pylint_data/messages/invalid-character-esc/good.py new file mode 100644 index 0000000..48ed2d8 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-esc/good.py @@ -0,0 +1 @@ +STRING = "Valid escape character \x1b" diff --git a/lib/common/pylint_data/messages/invalid-character-nul/details.md b/lib/common/pylint_data/messages/invalid-character-nul/details.md new file mode 100644 index 0000000..198fef5 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-nul/details.md @@ -0,0 +1,2 @@ +There\'s no need to use end-of-string characters. String objects +maintain their own length. diff --git a/lib/common/pylint_data/messages/invalid-character-nul/good.py b/lib/common/pylint_data/messages/invalid-character-nul/good.py new file mode 100644 index 0000000..97e1a3b --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-nul/good.py @@ -0,0 +1 @@ +STRING = "Valid nul terminator: \x00" diff --git a/lib/common/pylint_data/messages/invalid-character-nul/related.md b/lib/common/pylint_data/messages/invalid-character-nul/related.md new file mode 100644 index 0000000..a7ccb78 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-nul/related.md @@ -0,0 +1,2 @@ +- [Null terminator in + python](https://stackoverflow.com/a/24410304/2519059) diff --git a/lib/common/pylint_data/messages/invalid-character-sub/bad.py b/lib/common/pylint_data/messages/invalid-character-sub/bad.py new file mode 100644 index 0000000..313066d --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-sub/bad.py @@ -0,0 +1 @@ +STRING = "Invalid character sub " # [invalid-character-sub] diff --git a/lib/common/pylint_data/messages/invalid-character-sub/good.py b/lib/common/pylint_data/messages/invalid-character-sub/good.py new file mode 100644 index 0000000..5dd4aa4 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-sub/good.py @@ -0,0 +1 @@ +STRING = "Valid character sub x1A" diff --git a/lib/common/pylint_data/messages/invalid-character-zero-width-space/bad.py b/lib/common/pylint_data/messages/invalid-character-zero-width-space/bad.py new file mode 100644 index 0000000..99160fc --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-zero-width-space/bad.py @@ -0,0 +1 @@ +STRING = "Invalid character zero-width-space ​" # [invalid-character-zero-width-space] diff --git a/lib/common/pylint_data/messages/invalid-character-zero-width-space/good.py b/lib/common/pylint_data/messages/invalid-character-zero-width-space/good.py new file mode 100644 index 0000000..391bcf0 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-character-zero-width-space/good.py @@ -0,0 +1 @@ +STRING = "Valid character zero-width-space u200B" diff --git a/lib/common/pylint_data/messages/invalid-characters-in-docstring/details.md b/lib/common/pylint_data/messages/invalid-characters-in-docstring/details.md new file mode 100644 index 0000000..875b093 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-characters-in-docstring/details.md @@ -0,0 +1,3 @@ +This is a message linked to an internal problem in enchant. There\'s +nothing to change in your code, but maybe in pylint\'s configuration or +the way you installed the \'enchant\' system library. diff --git a/lib/common/pylint_data/messages/invalid-class-object/bad.py b/lib/common/pylint_data/messages/invalid-class-object/bad.py new file mode 100644 index 0000000..5c6a6f8 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-class-object/bad.py @@ -0,0 +1,5 @@ +class Apple: + pass + + +Apple.__class__ = 1 # [invalid-class-object] diff --git a/lib/common/pylint_data/messages/invalid-class-object/good.py b/lib/common/pylint_data/messages/invalid-class-object/good.py new file mode 100644 index 0000000..3b50097 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-class-object/good.py @@ -0,0 +1,9 @@ +class Apple: + pass + + +class RedDelicious: + pass + + +Apple.__class__ = RedDelicious diff --git a/lib/common/pylint_data/messages/invalid-enum-extension/bad.py b/lib/common/pylint_data/messages/invalid-enum-extension/bad.py new file mode 100644 index 0000000..2a2a010 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-enum-extension/bad.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class Color(Enum): + ORANGE = 1 + CHERRY = 2 + + +class Fruit(Color): # [invalid-enum-extension] + APPLE = 3 diff --git a/lib/common/pylint_data/messages/invalid-enum-extension/good.py b/lib/common/pylint_data/messages/invalid-enum-extension/good.py new file mode 100644 index 0000000..80a4115 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-enum-extension/good.py @@ -0,0 +1,12 @@ +from enum import Enum + + +class Color(Enum): + ORANGE = 1 + CHERRY = 2 + + +class Fruit(Enum): + ORANGE = 1 + CHERRY = 2 + APPLE = 3 diff --git a/lib/common/pylint_data/messages/invalid-envvar-default/bad.py b/lib/common/pylint_data/messages/invalid-envvar-default/bad.py new file mode 100644 index 0000000..9b564b9 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-envvar-default/bad.py @@ -0,0 +1,3 @@ +import os + +env = os.getenv("SECRET_KEY", 1) # [invalid-envvar-default] diff --git a/lib/common/pylint_data/messages/invalid-envvar-default/good.py b/lib/common/pylint_data/messages/invalid-envvar-default/good.py new file mode 100644 index 0000000..1039259 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-envvar-default/good.py @@ -0,0 +1,3 @@ +import os + +env = os.getenv("SECRET_KEY", "1") diff --git a/lib/common/pylint_data/messages/invalid-envvar-value/bad.py b/lib/common/pylint_data/messages/invalid-envvar-value/bad.py new file mode 100644 index 0000000..56e60fe --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-envvar-value/bad.py @@ -0,0 +1,3 @@ +import os + +os.getenv(1) # [invalid-envvar-value] diff --git a/lib/common/pylint_data/messages/invalid-envvar-value/good.py b/lib/common/pylint_data/messages/invalid-envvar-value/good.py new file mode 100644 index 0000000..fd082ec --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-envvar-value/good.py @@ -0,0 +1,3 @@ +import os + +os.getenv("1") diff --git a/lib/common/pylint_data/messages/invalid-field-call/bad.py b/lib/common/pylint_data/messages/invalid-field-call/bad.py new file mode 100644 index 0000000..56e3575 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-field-call/bad.py @@ -0,0 +1,16 @@ +from dataclasses import dataclass, field + + +@dataclass +class C: + a: float + b: float + c: float + + field(init=False) # [invalid-field-call] + + def __post_init__(self): + self.c = self.a + self.b + + +print(field(init=False)) # [invalid-field-call] diff --git a/lib/common/pylint_data/messages/invalid-field-call/good.py b/lib/common/pylint_data/messages/invalid-field-call/good.py new file mode 100644 index 0000000..8a8d891 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-field-call/good.py @@ -0,0 +1,17 @@ +from dataclasses import dataclass, field, make_dataclass + +C = make_dataclass( + "C", + [("x", int), "y", ("z", int, field(default=5))], + namespace={"add_one": lambda self: self.x + 1}, +) + + +@dataclass +class C: + a: float + b: float + c: float = field(init=False) + + def __post_init__(self): + self.c = self.a + self.b diff --git a/lib/common/pylint_data/messages/invalid-format-index/bad.py b/lib/common/pylint_data/messages/invalid-format-index/bad.py new file mode 100644 index 0000000..74e6502 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-format-index/bad.py @@ -0,0 +1,2 @@ +not_enough_fruits = ["apple"] +print('The second fruit is a {fruits[1]}'.format(fruits=not_enough_fruits)) # [invalid-format-index] diff --git a/lib/common/pylint_data/messages/invalid-format-index/good.py b/lib/common/pylint_data/messages/invalid-format-index/good.py new file mode 100644 index 0000000..6955798 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-format-index/good.py @@ -0,0 +1,2 @@ +enough_fruits = ["apple", "banana"] +print("The second fruit is a {fruits[1]}".format(fruits=enough_fruits)) diff --git a/lib/common/pylint_data/messages/invalid-format-returned/bad.py b/lib/common/pylint_data/messages/invalid-format-returned/bad.py new file mode 100644 index 0000000..8281084 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-format-returned/bad.py @@ -0,0 +1,5 @@ +class CustomFormat: + """__format__ returns """ + + def __format__(self, format_spec): # [invalid-format-returned] + return 1 diff --git a/lib/common/pylint_data/messages/invalid-format-returned/good.py b/lib/common/pylint_data/messages/invalid-format-returned/good.py new file mode 100644 index 0000000..66b13cc --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-format-returned/good.py @@ -0,0 +1,5 @@ +class CustomFormat: + """__format__ returns """ + + def __format__(self, format_spec): + return "hello!" diff --git a/lib/common/pylint_data/messages/invalid-getnewargs-ex-returned/bad.py b/lib/common/pylint_data/messages/invalid-getnewargs-ex-returned/bad.py new file mode 100644 index 0000000..e07bdd7 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-getnewargs-ex-returned/bad.py @@ -0,0 +1,5 @@ +class CustomGetNewArgsEx: + """__getnewargs_ex__ returns tuple with incorrect arg length""" + + def __getnewargs_ex__(self): # [invalid-getnewargs-ex-returned] + return (tuple(1), dict(x="y"), 1) diff --git a/lib/common/pylint_data/messages/invalid-getnewargs-ex-returned/good.py b/lib/common/pylint_data/messages/invalid-getnewargs-ex-returned/good.py new file mode 100644 index 0000000..9d6e31c --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-getnewargs-ex-returned/good.py @@ -0,0 +1,5 @@ +class CustomGetNewArgsEx: + """__getnewargs_ex__ returns """ + + def __getnewargs_ex__(self): + return ((1,), {"2": 2}) diff --git a/lib/common/pylint_data/messages/invalid-getnewargs-returned/bad.py b/lib/common/pylint_data/messages/invalid-getnewargs-returned/bad.py new file mode 100644 index 0000000..e82523b --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-getnewargs-returned/bad.py @@ -0,0 +1,5 @@ +class CustomGetNewArgs: + """__getnewargs__ returns an integer""" + + def __getnewargs__(self): # [invalid-getnewargs-returned] + return 1 diff --git a/lib/common/pylint_data/messages/invalid-getnewargs-returned/good.py b/lib/common/pylint_data/messages/invalid-getnewargs-returned/good.py new file mode 100644 index 0000000..e61376c --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-getnewargs-returned/good.py @@ -0,0 +1,5 @@ +class CustomGetNewArgs: + """__getnewargs__ returns """ + + def __getnewargs__(self): + return (1, 2) diff --git a/lib/common/pylint_data/messages/invalid-hash-returned/bad.py b/lib/common/pylint_data/messages/invalid-hash-returned/bad.py new file mode 100644 index 0000000..2c4508f --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-hash-returned/bad.py @@ -0,0 +1,5 @@ +class CustomHash: + """__hash__ returns dict""" + + def __hash__(self): # [invalid-hash-returned] + return {} diff --git a/lib/common/pylint_data/messages/invalid-hash-returned/good.py b/lib/common/pylint_data/messages/invalid-hash-returned/good.py new file mode 100644 index 0000000..b18ebc1 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-hash-returned/good.py @@ -0,0 +1,5 @@ +class CustomHash: + """__hash__ returns `int`""" + + def __hash__(self): + return 19 diff --git a/lib/common/pylint_data/messages/invalid-index-returned/bad.py b/lib/common/pylint_data/messages/invalid-index-returned/bad.py new file mode 100644 index 0000000..922abdf --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-index-returned/bad.py @@ -0,0 +1,5 @@ +class CustomIndex: + """__index__ returns a dict""" + + def __index__(self): # [invalid-index-returned] + return {"19": "19"} diff --git a/lib/common/pylint_data/messages/invalid-index-returned/good.py b/lib/common/pylint_data/messages/invalid-index-returned/good.py new file mode 100644 index 0000000..3b166be --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-index-returned/good.py @@ -0,0 +1,5 @@ +class CustomIndex: + """__index__ returns """ + + def __index__(self): + return 19 diff --git a/lib/common/pylint_data/messages/invalid-length-hint-returned/bad.py b/lib/common/pylint_data/messages/invalid-length-hint-returned/bad.py new file mode 100644 index 0000000..ef33425 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-length-hint-returned/bad.py @@ -0,0 +1,5 @@ +class CustomLengthHint: + """__length_hint__ returns non-int""" + + def __length_hint__(self): # [invalid-length-hint-returned] + return 3.0 diff --git a/lib/common/pylint_data/messages/invalid-length-hint-returned/good.py b/lib/common/pylint_data/messages/invalid-length-hint-returned/good.py new file mode 100644 index 0000000..8c80a29 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-length-hint-returned/good.py @@ -0,0 +1,5 @@ +class CustomLengthHint: + """__length_hint__ returns """ + + def __length_hint__(self): + return 10 diff --git a/lib/common/pylint_data/messages/invalid-length-returned/bad.py b/lib/common/pylint_data/messages/invalid-length-returned/bad.py new file mode 100644 index 0000000..2ae1396 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-length-returned/bad.py @@ -0,0 +1,6 @@ +class FruitBasket: + def __init__(self, fruits): + self.fruits = ["Apple", "Banana", "Orange"] + + def __len__(self): # [invalid-length-returned] + return -len(self.fruits) diff --git a/lib/common/pylint_data/messages/invalid-length-returned/good.py b/lib/common/pylint_data/messages/invalid-length-returned/good.py new file mode 100644 index 0000000..1af7189 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-length-returned/good.py @@ -0,0 +1,6 @@ +class FruitBasket: + def __init__(self, fruits): + self.fruits = ["Apple", "Banana", "Orange"] + + def __len__(self): + return len(self.fruits) diff --git a/lib/common/pylint_data/messages/invalid-match-args-definition/bad.py b/lib/common/pylint_data/messages/invalid-match-args-definition/bad.py new file mode 100644 index 0000000..de18156 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-match-args-definition/bad.py @@ -0,0 +1,6 @@ +class Book: + __match_args__ = ["title", "year"] # [invalid-match-args-definition] + + def __init__(self, title, year): + self.title = title + self.year = year diff --git a/lib/common/pylint_data/messages/invalid-match-args-definition/good.py b/lib/common/pylint_data/messages/invalid-match-args-definition/good.py new file mode 100644 index 0000000..7edfc8a --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-match-args-definition/good.py @@ -0,0 +1,6 @@ +class Book: + __match_args__ = ("title", "year") + + def __init__(self, title, year): + self.title = title + self.year = year diff --git a/lib/common/pylint_data/messages/invalid-match-args-definition/related.md b/lib/common/pylint_data/messages/invalid-match-args-definition/related.md new file mode 100644 index 0000000..bffd34c --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-match-args-definition/related.md @@ -0,0 +1,2 @@ +- [Python + documentation](https://docs.python.org/3/reference/datamodel.html#customizing-positional-arguments-in-class-pattern-matching) diff --git a/lib/common/pylint_data/messages/invalid-metaclass/bad.py b/lib/common/pylint_data/messages/invalid-metaclass/bad.py new file mode 100644 index 0000000..301b4f2 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-metaclass/bad.py @@ -0,0 +1,2 @@ +class Apple(metaclass=int): # [invalid-metaclass] + pass diff --git a/lib/common/pylint_data/messages/invalid-metaclass/good.py b/lib/common/pylint_data/messages/invalid-metaclass/good.py new file mode 100644 index 0000000..07c907f --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-metaclass/good.py @@ -0,0 +1,6 @@ +class Plant: + pass + + +class Apple(Plant): + pass diff --git a/lib/common/pylint_data/messages/invalid-name/bad.py b/lib/common/pylint_data/messages/invalid-name/bad.py new file mode 100644 index 0000000..b40ee47 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-name/bad.py @@ -0,0 +1,7 @@ +class cat: # [invalid-name] + def Meow(self, NUMBER_OF_MEOW): # [invalid-name, invalid-name] + print("Meow" * NUMBER_OF_MEOW) + return NUMBER_OF_MEOW + + +Cat = cat().Meow(42) # [invalid-name] diff --git a/lib/common/pylint_data/messages/invalid-name/details.md b/lib/common/pylint_data/messages/invalid-name/details.md new file mode 100644 index 0000000..0b987d2 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-name/details.md @@ -0,0 +1,16 @@ +By default, Pylint will enforce +[PEP8](https://peps.python.org/pep-0008)-suggested names. + +The following naming suggestions are used for Exercism exercises: + +- modules (code files): snake_case +- constants: UPPER_CASE +- variables: snake_case (minimum of 3 letters) +- functions: snake_case +- arguments: snake_case +- attributes: snake_case +- classes: PascalCase +- class attributes: any (no specific format) +- class constants: UPPER_CASE +- class methods: snake_case +- inline variables and loop variables: snake_case diff --git a/lib/common/pylint_data/messages/invalid-name/good.py b/lib/common/pylint_data/messages/invalid-name/good.py new file mode 100644 index 0000000..d747a72 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-name/good.py @@ -0,0 +1,7 @@ +class Cat: + def meow(self, number_of_meow): + print("Meow" * number_of_meow) + return number_of_meow + + +CAT = Cat().meow(42) diff --git a/lib/common/pylint_data/messages/invalid-overridden-method/bad.py b/lib/common/pylint_data/messages/invalid-overridden-method/bad.py new file mode 100644 index 0000000..c7e250f --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-overridden-method/bad.py @@ -0,0 +1,8 @@ +class Fruit: + async def bore(self, insect): + insect.eat(self) + + +class Apple(Fruit): + def bore(self, insect): # [invalid-overridden-method] + insect.eat(self) diff --git a/lib/common/pylint_data/messages/invalid-overridden-method/good.py b/lib/common/pylint_data/messages/invalid-overridden-method/good.py new file mode 100644 index 0000000..3206637 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-overridden-method/good.py @@ -0,0 +1,8 @@ +class Fruit: + async def bore(self, insect): + insect.eat(self) + + +class Apple(Fruit): + async def bore(self, insect): + insect.eat(self) diff --git a/lib/common/pylint_data/messages/invalid-repr-returned/bad.py b/lib/common/pylint_data/messages/invalid-repr-returned/bad.py new file mode 100644 index 0000000..74ab44b --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-repr-returned/bad.py @@ -0,0 +1,5 @@ +class CustomRepr: + """__repr__ returns """ + + def __repr__(self): # [invalid-repr-returned] + return 1 diff --git a/lib/common/pylint_data/messages/invalid-repr-returned/good.py b/lib/common/pylint_data/messages/invalid-repr-returned/good.py new file mode 100644 index 0000000..b9e2b54 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-repr-returned/good.py @@ -0,0 +1,5 @@ +class CustomRepr: + """__repr__ returns """ + + def __repr__(self): + return "apples" diff --git a/lib/common/pylint_data/messages/invalid-sequence-index/bad.py b/lib/common/pylint_data/messages/invalid-sequence-index/bad.py new file mode 100644 index 0000000..c13a374 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-sequence-index/bad.py @@ -0,0 +1,2 @@ +fruits = ["apple", "banana", "orange"] +print(fruits["apple"]) # [invalid-sequence-index] diff --git a/lib/common/pylint_data/messages/invalid-sequence-index/details.md b/lib/common/pylint_data/messages/invalid-sequence-index/details.md new file mode 100644 index 0000000..6678cef --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-sequence-index/details.md @@ -0,0 +1,3 @@ +Be careful with `[True]` or `[False]` as sequence index, since `True` +and `False` will respectively be evaluated as `1` and `0` and will bring +the second element of the list and the first without erroring. diff --git a/lib/common/pylint_data/messages/invalid-sequence-index/good.py b/lib/common/pylint_data/messages/invalid-sequence-index/good.py new file mode 100644 index 0000000..840f3c3 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-sequence-index/good.py @@ -0,0 +1,2 @@ +fruits = ["apple", "banana", "orange"] +print(fruits[0]) diff --git a/lib/common/pylint_data/messages/invalid-slice-index/bad.py b/lib/common/pylint_data/messages/invalid-slice-index/bad.py new file mode 100644 index 0000000..3a35b4a --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-slice-index/bad.py @@ -0,0 +1,3 @@ +LETTERS = ["a", "b", "c", "d"] + +FIRST_THREE = LETTERS[:"3"] # [invalid-slice-index] diff --git a/lib/common/pylint_data/messages/invalid-slice-index/good.py b/lib/common/pylint_data/messages/invalid-slice-index/good.py new file mode 100644 index 0000000..e17f31c --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-slice-index/good.py @@ -0,0 +1,3 @@ +LETTERS = ["a", "b", "c", "d"] + +FIRST_THREE = LETTERS[:3] diff --git a/lib/common/pylint_data/messages/invalid-slice-step/bad.py b/lib/common/pylint_data/messages/invalid-slice-step/bad.py new file mode 100644 index 0000000..a860ce1 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-slice-step/bad.py @@ -0,0 +1,3 @@ +LETTERS = ["a", "b", "c", "d"] + +LETTERS[::0] # [invalid-slice-step] diff --git a/lib/common/pylint_data/messages/invalid-slice-step/good.py b/lib/common/pylint_data/messages/invalid-slice-step/good.py new file mode 100644 index 0000000..c81d803 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-slice-step/good.py @@ -0,0 +1,3 @@ +LETTERS = ["a", "b", "c", "d"] + +LETTERS[::2] diff --git a/lib/common/pylint_data/messages/invalid-slots-object/bad.py b/lib/common/pylint_data/messages/invalid-slots-object/bad.py new file mode 100644 index 0000000..03690cf --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-slots-object/bad.py @@ -0,0 +1,2 @@ +class Person: + __slots__ = ("name", 3) # [invalid-slots-object] diff --git a/lib/common/pylint_data/messages/invalid-slots-object/good.py b/lib/common/pylint_data/messages/invalid-slots-object/good.py new file mode 100644 index 0000000..823a971 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-slots-object/good.py @@ -0,0 +1,2 @@ +class Person: + __slots__ = ("name", "surname") diff --git a/lib/common/pylint_data/messages/invalid-slots-object/related.md b/lib/common/pylint_data/messages/invalid-slots-object/related.md new file mode 100644 index 0000000..32e2458 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-slots-object/related.md @@ -0,0 +1,2 @@ +- [Documentation for + \_\_slots\_\_](https://docs.python.org/3/reference/datamodel.html#slots) diff --git a/lib/common/pylint_data/messages/invalid-slots/bad.py b/lib/common/pylint_data/messages/invalid-slots/bad.py new file mode 100644 index 0000000..d1184c8 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-slots/bad.py @@ -0,0 +1,2 @@ +class Person: # [invalid-slots] + __slots__ = 42 diff --git a/lib/common/pylint_data/messages/invalid-slots/good.py b/lib/common/pylint_data/messages/invalid-slots/good.py new file mode 100644 index 0000000..b788135 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-slots/good.py @@ -0,0 +1,2 @@ +class Person: + __slots__ = ("name", "age") diff --git a/lib/common/pylint_data/messages/invalid-star-assignment-target/bad.py b/lib/common/pylint_data/messages/invalid-star-assignment-target/bad.py new file mode 100644 index 0000000..4769285 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-star-assignment-target/bad.py @@ -0,0 +1 @@ +*fruit = ["apple", "banana", "orange"] # [invalid-star-assignment-target] diff --git a/lib/common/pylint_data/messages/invalid-star-assignment-target/good.py b/lib/common/pylint_data/messages/invalid-star-assignment-target/good.py new file mode 100644 index 0000000..b174bcd --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-star-assignment-target/good.py @@ -0,0 +1 @@ +fruit = ["apple", "banana", "orange"] diff --git a/lib/common/pylint_data/messages/invalid-str-returned/bad.py b/lib/common/pylint_data/messages/invalid-str-returned/bad.py new file mode 100644 index 0000000..97914d2 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-str-returned/bad.py @@ -0,0 +1,5 @@ +class CustomStr: + """__str__ returns int""" + + def __str__(self): # [invalid-str-returned] + return 1 diff --git a/lib/common/pylint_data/messages/invalid-str-returned/good.py b/lib/common/pylint_data/messages/invalid-str-returned/good.py new file mode 100644 index 0000000..580a3f5 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-str-returned/good.py @@ -0,0 +1,5 @@ +class CustomStr: + """__str__ returns """ + + def __str__(self): + return "oranges" diff --git a/lib/common/pylint_data/messages/invalid-unary-operand-type/bad.py b/lib/common/pylint_data/messages/invalid-unary-operand-type/bad.py new file mode 100644 index 0000000..b011951 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-unary-operand-type/bad.py @@ -0,0 +1,3 @@ +cherries = 10 +eaten_cherries = int +cherries = -eaten_cherries # [invalid-unary-operand-type] diff --git a/lib/common/pylint_data/messages/invalid-unary-operand-type/good.py b/lib/common/pylint_data/messages/invalid-unary-operand-type/good.py new file mode 100644 index 0000000..a9a47f5 --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-unary-operand-type/good.py @@ -0,0 +1,3 @@ +cherries = 10 +eaten_cherries = 2 +cherries -= eaten_cherries diff --git a/lib/common/pylint_data/messages/invalid-unicode-codec/details.md b/lib/common/pylint_data/messages/invalid-unicode-codec/details.md new file mode 100644 index 0000000..5b9d95d --- /dev/null +++ b/lib/common/pylint_data/messages/invalid-unicode-codec/details.md @@ -0,0 +1,2 @@ +This message is a placeholder for a potential future issue with unicode +codecs. diff --git a/lib/common/pylint_data/messages/isinstance-second-argument-not-valid-type/bad.py b/lib/common/pylint_data/messages/isinstance-second-argument-not-valid-type/bad.py new file mode 100644 index 0000000..5fb3b83 --- /dev/null +++ b/lib/common/pylint_data/messages/isinstance-second-argument-not-valid-type/bad.py @@ -0,0 +1 @@ +isinstance("apples and oranges", hex) # [isinstance-second-argument-not-valid-type] diff --git a/lib/common/pylint_data/messages/isinstance-second-argument-not-valid-type/good.py b/lib/common/pylint_data/messages/isinstance-second-argument-not-valid-type/good.py new file mode 100644 index 0000000..c1df5fc --- /dev/null +++ b/lib/common/pylint_data/messages/isinstance-second-argument-not-valid-type/good.py @@ -0,0 +1 @@ +isinstance("apples and oranges", str) diff --git a/lib/common/pylint_data/messages/keyword-arg-before-vararg/bad.py b/lib/common/pylint_data/messages/keyword-arg-before-vararg/bad.py new file mode 100644 index 0000000..562724e --- /dev/null +++ b/lib/common/pylint_data/messages/keyword-arg-before-vararg/bad.py @@ -0,0 +1,2 @@ +def func(x=None, *args): # [keyword-arg-before-vararg] + return [x, *args] diff --git a/lib/common/pylint_data/messages/keyword-arg-before-vararg/good.py b/lib/common/pylint_data/messages/keyword-arg-before-vararg/good.py new file mode 100644 index 0000000..e1f14ba --- /dev/null +++ b/lib/common/pylint_data/messages/keyword-arg-before-vararg/good.py @@ -0,0 +1,2 @@ +def func(*args, x=None): + return [*args, x] diff --git a/lib/common/pylint_data/messages/kwarg-superseded-by-positional-arg/bad.py b/lib/common/pylint_data/messages/kwarg-superseded-by-positional-arg/bad.py new file mode 100644 index 0000000..edc90a2 --- /dev/null +++ b/lib/common/pylint_data/messages/kwarg-superseded-by-positional-arg/bad.py @@ -0,0 +1,6 @@ +def print_name(name="Sarah", /, **kwds): + print(name) + + +print_name(name="Jacob") # [kwarg-superseded-by-positional-arg] +# Will print "Sarah" diff --git a/lib/common/pylint_data/messages/kwarg-superseded-by-positional-arg/good.py b/lib/common/pylint_data/messages/kwarg-superseded-by-positional-arg/good.py new file mode 100644 index 0000000..d795a01 --- /dev/null +++ b/lib/common/pylint_data/messages/kwarg-superseded-by-positional-arg/good.py @@ -0,0 +1,6 @@ +def print_name(name="Sarah", /, **kwds): + print(name) + + +print_name("Jacob") +# Will print "Jacob" diff --git a/lib/common/pylint_data/messages/line-too-long/bad.py b/lib/common/pylint_data/messages/line-too-long/bad.py new file mode 100644 index 0000000..94e9042 --- /dev/null +++ b/lib/common/pylint_data/messages/line-too-long/bad.py @@ -0,0 +1,2 @@ +# +1: [line-too-long] +FRUIT = ["apricot", "blackcurrant", "cantaloupe", "dragon fruit", "elderberry", "fig", "grapefruit", ] diff --git a/lib/common/pylint_data/messages/line-too-long/details.md b/lib/common/pylint_data/messages/line-too-long/details.md new file mode 100644 index 0000000..5b85c16 --- /dev/null +++ b/lib/common/pylint_data/messages/line-too-long/details.md @@ -0,0 +1,10 @@ +Pragma controls such as `# pylint: disable=all` are not counted toward +line length for the purposes of this message. + +If you attempt to disable this message via +`# pylint: disable=line-too-long` in a module with no code, you may +receive a message for `useless-suppression`. This is a false positive of +`useless-suppression` we can\'t easily fix. + +See for more +information. diff --git a/lib/common/pylint_data/messages/line-too-long/good.py b/lib/common/pylint_data/messages/line-too-long/good.py new file mode 100644 index 0000000..091c8a3 --- /dev/null +++ b/lib/common/pylint_data/messages/line-too-long/good.py @@ -0,0 +1,9 @@ +FRUIT = [ + "apricot", + "blackcurrant", + "cantaloupe", + "dragon fruit", + "elderberry", + "fig", + "grapefruit", +] diff --git a/lib/common/pylint_data/messages/literal-comparison/bad.py b/lib/common/pylint_data/messages/literal-comparison/bad.py new file mode 100644 index 0000000..154d7d6 --- /dev/null +++ b/lib/common/pylint_data/messages/literal-comparison/bad.py @@ -0,0 +1,2 @@ +def is_an_orange(fruit): + return fruit is "orange" # [literal-comparison] diff --git a/lib/common/pylint_data/messages/literal-comparison/good.py b/lib/common/pylint_data/messages/literal-comparison/good.py new file mode 100644 index 0000000..786aeaf --- /dev/null +++ b/lib/common/pylint_data/messages/literal-comparison/good.py @@ -0,0 +1,2 @@ +def is_an_orange(fruit): + return fruit == "orange" diff --git a/lib/common/pylint_data/messages/literal-comparison/related.md b/lib/common/pylint_data/messages/literal-comparison/related.md new file mode 100644 index 0000000..89e5db4 --- /dev/null +++ b/lib/common/pylint_data/messages/literal-comparison/related.md @@ -0,0 +1,2 @@ +- [Comparison operations in + Python](https://docs.python.org/3/library/stdtypes.html#comparisons) diff --git a/lib/common/pylint_data/messages/locally-disabled/bad.py b/lib/common/pylint_data/messages/locally-disabled/bad.py new file mode 100644 index 0000000..1a33078 --- /dev/null +++ b/lib/common/pylint_data/messages/locally-disabled/bad.py @@ -0,0 +1,8 @@ +def wizard_spells(spell_book): + # pylint: disable=maybe-no-member # [locally-disabled] + for spell in spell_book: + print(f"Abracadabra! {spell}.") + + +spell_list = ["Levitation", "Invisibility", "Fireball", "Teleportation"] +wizard_spells(spell_list) diff --git a/lib/common/pylint_data/messages/locally-disabled/good.py b/lib/common/pylint_data/messages/locally-disabled/good.py new file mode 100644 index 0000000..1bf60bc --- /dev/null +++ b/lib/common/pylint_data/messages/locally-disabled/good.py @@ -0,0 +1,7 @@ +def wizard_spells(spell_book): + for spell in spell_book: + print(f"Abracadabra! {spell}.") + + +spell_list = ["Levitation", "Invisibility", "Fireball", "Teleportation"] +wizard_spells(spell_list) diff --git a/lib/common/pylint_data/messages/logging-format-interpolation/bad.py b/lib/common/pylint_data/messages/logging-format-interpolation/bad.py new file mode 100644 index 0000000..dd828e1 --- /dev/null +++ b/lib/common/pylint_data/messages/logging-format-interpolation/bad.py @@ -0,0 +1,5 @@ +import logging +import sys + +# +1: [logging-format-interpolation] +logging.error("Python version: {}".format(sys.version)) diff --git a/lib/common/pylint_data/messages/logging-format-interpolation/details.md b/lib/common/pylint_data/messages/logging-format-interpolation/details.md new file mode 100644 index 0000000..698bb46 --- /dev/null +++ b/lib/common/pylint_data/messages/logging-format-interpolation/details.md @@ -0,0 +1,3 @@ +Another reasonable option is to use f-string. If you want to do that, +you need to enable `logging-format-interpolation` and disable +`logging-fstring-interpolation`. diff --git a/lib/common/pylint_data/messages/logging-format-interpolation/good.py b/lib/common/pylint_data/messages/logging-format-interpolation/good.py new file mode 100644 index 0000000..a993d0d --- /dev/null +++ b/lib/common/pylint_data/messages/logging-format-interpolation/good.py @@ -0,0 +1,4 @@ +import logging +import sys + +logging.error("Python version: %s", sys.version) diff --git a/lib/common/pylint_data/messages/logging-format-interpolation/related.md b/lib/common/pylint_data/messages/logging-format-interpolation/related.md new file mode 100644 index 0000000..04769cf --- /dev/null +++ b/lib/common/pylint_data/messages/logging-format-interpolation/related.md @@ -0,0 +1,4 @@ +- [logging variable + data](https://docs.python.org/3/howto/logging.html#logging-variable-data) +- [Rationale for the message on + stackoverflow](https://stackoverflow.com/a/34634301/2519059) diff --git a/lib/common/pylint_data/messages/logging-format-truncated/bad.py b/lib/common/pylint_data/messages/logging-format-truncated/bad.py new file mode 100644 index 0000000..2100d22 --- /dev/null +++ b/lib/common/pylint_data/messages/logging-format-truncated/bad.py @@ -0,0 +1,4 @@ +import logging +import sys + +logging.warning("Python version: %", sys.version) # [logging-format-truncated] diff --git a/lib/common/pylint_data/messages/logging-format-truncated/good.py b/lib/common/pylint_data/messages/logging-format-truncated/good.py new file mode 100644 index 0000000..c929471 --- /dev/null +++ b/lib/common/pylint_data/messages/logging-format-truncated/good.py @@ -0,0 +1,4 @@ +import logging +import sys + +logging.warning("Python version: %s", sys.version) diff --git a/lib/common/pylint_data/messages/logging-fstring-interpolation/bad.py b/lib/common/pylint_data/messages/logging-fstring-interpolation/bad.py new file mode 100644 index 0000000..773435f --- /dev/null +++ b/lib/common/pylint_data/messages/logging-fstring-interpolation/bad.py @@ -0,0 +1,4 @@ +import logging +import sys + +logging.error(f"Python version: {sys.version}") # [logging-fstring-interpolation] diff --git a/lib/common/pylint_data/messages/logging-fstring-interpolation/details.md b/lib/common/pylint_data/messages/logging-fstring-interpolation/details.md new file mode 100644 index 0000000..ea395e4 --- /dev/null +++ b/lib/common/pylint_data/messages/logging-fstring-interpolation/details.md @@ -0,0 +1,2 @@ +This message permits to allow f-string in logging and still be warned of +`logging-format-interpolation`. diff --git a/lib/common/pylint_data/messages/logging-fstring-interpolation/good.py b/lib/common/pylint_data/messages/logging-fstring-interpolation/good.py new file mode 100644 index 0000000..a993d0d --- /dev/null +++ b/lib/common/pylint_data/messages/logging-fstring-interpolation/good.py @@ -0,0 +1,4 @@ +import logging +import sys + +logging.error("Python version: %s", sys.version) diff --git a/lib/common/pylint_data/messages/logging-fstring-interpolation/related.md b/lib/common/pylint_data/messages/logging-fstring-interpolation/related.md new file mode 100644 index 0000000..799f3c4 --- /dev/null +++ b/lib/common/pylint_data/messages/logging-fstring-interpolation/related.md @@ -0,0 +1,3 @@ +- [logging variable + data](https://docs.python.org/3/howto/logging.html#logging-variable-data) +- [Rationale](https://stackoverflow.com/questions/34619790) diff --git a/lib/common/pylint_data/messages/logging-not-lazy/bad.py b/lib/common/pylint_data/messages/logging-not-lazy/bad.py new file mode 100644 index 0000000..7ba0631 --- /dev/null +++ b/lib/common/pylint_data/messages/logging-not-lazy/bad.py @@ -0,0 +1,7 @@ +import logging + +try: + function() +except Exception as e: + logging.error("Error occurred: %s" % e) # [logging-not-lazy] + raise diff --git a/lib/common/pylint_data/messages/logging-not-lazy/details.md b/lib/common/pylint_data/messages/logging-not-lazy/details.md new file mode 100644 index 0000000..59c03ef --- /dev/null +++ b/lib/common/pylint_data/messages/logging-not-lazy/details.md @@ -0,0 +1,3 @@ +Another reasonable option is to use f-strings. If you want to do that, +you need to enable `logging-not-lazy` and disable +`logging-fstring-interpolation`. diff --git a/lib/common/pylint_data/messages/logging-not-lazy/good.py b/lib/common/pylint_data/messages/logging-not-lazy/good.py new file mode 100644 index 0000000..ede0050 --- /dev/null +++ b/lib/common/pylint_data/messages/logging-not-lazy/good.py @@ -0,0 +1,7 @@ +import logging + +try: + function() +except Exception as e: + logging.error("Error occurred: %s", e) + raise diff --git a/lib/common/pylint_data/messages/logging-not-lazy/related.md b/lib/common/pylint_data/messages/logging-not-lazy/related.md new file mode 100644 index 0000000..5b74b6e --- /dev/null +++ b/lib/common/pylint_data/messages/logging-not-lazy/related.md @@ -0,0 +1,4 @@ +- [Logging variable + data](https://docs.python.org/3/howto/logging.html#logging-variable-data) +- [Rationale for the message on + stackoverflow](https://stackoverflow.com/a/34634301/2519059) diff --git a/lib/common/pylint_data/messages/logging-too-few-args/bad.py b/lib/common/pylint_data/messages/logging-too-few-args/bad.py new file mode 100644 index 0000000..a20b83b --- /dev/null +++ b/lib/common/pylint_data/messages/logging-too-few-args/bad.py @@ -0,0 +1,7 @@ +import logging + +try: + function() +except Exception as e: + logging.error("%s error occurred: %s", e) # [logging-too-few-args] + raise diff --git a/lib/common/pylint_data/messages/logging-too-few-args/good.py b/lib/common/pylint_data/messages/logging-too-few-args/good.py new file mode 100644 index 0000000..4812aec --- /dev/null +++ b/lib/common/pylint_data/messages/logging-too-few-args/good.py @@ -0,0 +1,7 @@ +import logging + +try: + function() +except Exception as e: + logging.error("%s error occurred: %s", type(e), e) + raise diff --git a/lib/common/pylint_data/messages/logging-too-many-args/bad.py b/lib/common/pylint_data/messages/logging-too-many-args/bad.py new file mode 100644 index 0000000..03753f6 --- /dev/null +++ b/lib/common/pylint_data/messages/logging-too-many-args/bad.py @@ -0,0 +1,7 @@ +import logging + +try: + function() +except Exception as e: + logging.error("Error occurred: %s", type(e), e) # [logging-too-many-args] + raise diff --git a/lib/common/pylint_data/messages/logging-too-many-args/good.py b/lib/common/pylint_data/messages/logging-too-many-args/good.py new file mode 100644 index 0000000..4812aec --- /dev/null +++ b/lib/common/pylint_data/messages/logging-too-many-args/good.py @@ -0,0 +1,7 @@ +import logging + +try: + function() +except Exception as e: + logging.error("%s error occurred: %s", type(e), e) + raise diff --git a/lib/common/pylint_data/messages/logging-unsupported-format/bad.py b/lib/common/pylint_data/messages/logging-unsupported-format/bad.py new file mode 100644 index 0000000..1f36a0d --- /dev/null +++ b/lib/common/pylint_data/messages/logging-unsupported-format/bad.py @@ -0,0 +1,3 @@ +import logging + +logging.info("%s %y !", "Hello", "World") # [logging-unsupported-format] diff --git a/lib/common/pylint_data/messages/logging-unsupported-format/good.py b/lib/common/pylint_data/messages/logging-unsupported-format/good.py new file mode 100644 index 0000000..53e06d3 --- /dev/null +++ b/lib/common/pylint_data/messages/logging-unsupported-format/good.py @@ -0,0 +1,3 @@ +import logging + +logging.info("%s %s !", "Hello", "World") diff --git a/lib/common/pylint_data/messages/lost-exception/bad.py b/lib/common/pylint_data/messages/lost-exception/bad.py new file mode 100644 index 0000000..c6e5c02 --- /dev/null +++ b/lib/common/pylint_data/messages/lost-exception/bad.py @@ -0,0 +1,12 @@ +class FasterThanTheSpeedOfLightError(ZeroDivisionError): + def __init__(self): + super().__init__("You can't go faster than the speed of light !") + + +def calculate_speed(distance: float, time: float) -> float: + try: + return distance / time + except ZeroDivisionError as e: + raise FasterThanTheSpeedOfLightError() from e + finally: + return 299792458 # [lost-exception] diff --git a/lib/common/pylint_data/messages/lost-exception/good.py b/lib/common/pylint_data/messages/lost-exception/good.py new file mode 100644 index 0000000..a269492 --- /dev/null +++ b/lib/common/pylint_data/messages/lost-exception/good.py @@ -0,0 +1,10 @@ +class FasterThanTheSpeedOfLightError(ZeroDivisionError): + def __init__(self): + super().__init__("You can't go faster than the speed of light !") + + +def calculate_speed(distance: float, time: float) -> float: + try: + return distance / time + except ZeroDivisionError as e: + raise FasterThanTheSpeedOfLightError() from e diff --git a/lib/common/pylint_data/messages/magic-value-comparison/bad.py b/lib/common/pylint_data/messages/magic-value-comparison/bad.py new file mode 100644 index 0000000..eef9e5d --- /dev/null +++ b/lib/common/pylint_data/messages/magic-value-comparison/bad.py @@ -0,0 +1,10 @@ +import random + +measurement = random.randint(0, 200) +above_threshold = False +i = 0 +while i < 5: # [magic-value-comparison] + above_threshold = measurement > 100 # [magic-value-comparison] + if above_threshold: + break + measurement = random.randint(0, 200) diff --git a/lib/common/pylint_data/messages/magic-value-comparison/good.py b/lib/common/pylint_data/messages/magic-value-comparison/good.py new file mode 100644 index 0000000..4b89609 --- /dev/null +++ b/lib/common/pylint_data/messages/magic-value-comparison/good.py @@ -0,0 +1,15 @@ +import random + +MAX_NUM_OF_ITERATIONS = 5 +THRESHOLD_VAL = 100 +MIN_MEASUREMENT_VAL = 0 +MAX_MEASUREMENT_VAL = 200 + +measurement = random.randint(MIN_MEASUREMENT_VAL, MAX_MEASUREMENT_VAL) +above_threshold = False +i = 0 +while i < MAX_NUM_OF_ITERATIONS: + above_threshold = measurement > THRESHOLD_VAL + if above_threshold: + break + measurement = random.randint(MIN_MEASUREMENT_VAL, MAX_MEASUREMENT_VAL) diff --git a/lib/common/pylint_data/messages/match-class-bind-self/bad.py b/lib/common/pylint_data/messages/match-class-bind-self/bad.py new file mode 100644 index 0000000..a2919b7 --- /dev/null +++ b/lib/common/pylint_data/messages/match-class-bind-self/bad.py @@ -0,0 +1,14 @@ +class Book: + __match_args__ = ("title", "year") + + def __init__(self, title, year): + self.title = title + self.year = year + + +def func(item: Book): + match item: + case Book(title=str(title)): # [match-class-bind-self] + ... + case Book(year=int(year)): # [match-class-bind-self] + ... diff --git a/lib/common/pylint_data/messages/match-class-bind-self/good.py b/lib/common/pylint_data/messages/match-class-bind-self/good.py new file mode 100644 index 0000000..df2eced --- /dev/null +++ b/lib/common/pylint_data/messages/match-class-bind-self/good.py @@ -0,0 +1,14 @@ +class Book: + __match_args__ = ("title", "year") + + def __init__(self, title, year): + self.title = title + self.year = year + + +def func(item: Book): + match item: + case Book(title=str() as title): + ... + case Book(year=int() as year): + ... diff --git a/lib/common/pylint_data/messages/match-class-bind-self/related.md b/lib/common/pylint_data/messages/match-class-bind-self/related.md new file mode 100644 index 0000000..6c207f8 --- /dev/null +++ b/lib/common/pylint_data/messages/match-class-bind-self/related.md @@ -0,0 +1,2 @@ +- [Python + documentation](https://docs.python.org/3/reference/compound_stmts.html#class-patterns) diff --git a/lib/common/pylint_data/messages/match-class-positional-attributes/bad.py b/lib/common/pylint_data/messages/match-class-positional-attributes/bad.py new file mode 100644 index 0000000..69013b3 --- /dev/null +++ b/lib/common/pylint_data/messages/match-class-positional-attributes/bad.py @@ -0,0 +1,12 @@ +class Book: + __match_args__ = ("title", "year") + + def __init__(self, title, year): + self.title = title + self.year = year + + +def func(item: Book): + match item: + case Book("abc", 2000): # [match-class-positional-attributes] + ... diff --git a/lib/common/pylint_data/messages/match-class-positional-attributes/good.py b/lib/common/pylint_data/messages/match-class-positional-attributes/good.py new file mode 100644 index 0000000..93c567b --- /dev/null +++ b/lib/common/pylint_data/messages/match-class-positional-attributes/good.py @@ -0,0 +1,12 @@ +class Book: + __match_args__ = ("title", "year") + + def __init__(self, title, year): + self.title = title + self.year = year + + +def func(item: Book): + match item: + case Book(title="abc", year=2000): + ... diff --git a/lib/common/pylint_data/messages/match-class-positional-attributes/related.md b/lib/common/pylint_data/messages/match-class-positional-attributes/related.md new file mode 100644 index 0000000..6c207f8 --- /dev/null +++ b/lib/common/pylint_data/messages/match-class-positional-attributes/related.md @@ -0,0 +1,2 @@ +- [Python + documentation](https://docs.python.org/3/reference/compound_stmts.html#class-patterns) diff --git a/lib/common/pylint_data/messages/method-cache-max-size-none/bad.py b/lib/common/pylint_data/messages/method-cache-max-size-none/bad.py new file mode 100644 index 0000000..7bef647 --- /dev/null +++ b/lib/common/pylint_data/messages/method-cache-max-size-none/bad.py @@ -0,0 +1,12 @@ +import functools + + +class Fibonnaci: + def __init__(self): + self.result = [] + + @functools.lru_cache(maxsize=None) # [method-cache-max-size-none] + def fibonacci(self, n): + if n in {0, 1}: + self.result.append(n) + self.result.append(self.fibonacci(n - 1) + self.fibonacci(n - 2)) diff --git a/lib/common/pylint_data/messages/method-cache-max-size-none/good.py b/lib/common/pylint_data/messages/method-cache-max-size-none/good.py new file mode 100644 index 0000000..1ba4136 --- /dev/null +++ b/lib/common/pylint_data/messages/method-cache-max-size-none/good.py @@ -0,0 +1,16 @@ +import functools + + +@functools.cache +def cached_fibonacci(n): + if n in {0, 1}: + return n + return cached_fibonacci(n - 1) + cached_fibonacci(n - 2) + + +class Fibonnaci: + def __init__(self): + self.result = [] + + def fibonacci(self, n): + self.result.append(cached_fibonacci(n)) diff --git a/lib/common/pylint_data/messages/method-check-failed/details.md b/lib/common/pylint_data/messages/method-check-failed/details.md new file mode 100644 index 0000000..0ef4d8c --- /dev/null +++ b/lib/common/pylint_data/messages/method-check-failed/details.md @@ -0,0 +1,2 @@ +This is a message linked to an internal problem in pylint. There\'s +nothing to change in your code. diff --git a/lib/common/pylint_data/messages/method-hidden/bad.py b/lib/common/pylint_data/messages/method-hidden/bad.py new file mode 100644 index 0000000..5dc1130 --- /dev/null +++ b/lib/common/pylint_data/messages/method-hidden/bad.py @@ -0,0 +1,6 @@ +class Fruit: + def __init__(self, vitamins): + self.vitamins = vitamins + + def vitamins(self): # [method-hidden] + pass diff --git a/lib/common/pylint_data/messages/method-hidden/good.py b/lib/common/pylint_data/messages/method-hidden/good.py new file mode 100644 index 0000000..cfc23c2 --- /dev/null +++ b/lib/common/pylint_data/messages/method-hidden/good.py @@ -0,0 +1,6 @@ +class Fruit: + def __init__(self, vitamins): + self.vitamins = vitamins + + def antioxidants(self): + pass diff --git a/lib/common/pylint_data/messages/misplaced-bare-raise/bad.py b/lib/common/pylint_data/messages/misplaced-bare-raise/bad.py new file mode 100644 index 0000000..c7fc0ee --- /dev/null +++ b/lib/common/pylint_data/messages/misplaced-bare-raise/bad.py @@ -0,0 +1,3 @@ +def validate_positive(x): + if x <= 0: + raise # [misplaced-bare-raise] diff --git a/lib/common/pylint_data/messages/misplaced-bare-raise/good.py b/lib/common/pylint_data/messages/misplaced-bare-raise/good.py new file mode 100644 index 0000000..bc2333c --- /dev/null +++ b/lib/common/pylint_data/messages/misplaced-bare-raise/good.py @@ -0,0 +1,3 @@ +def validate_positive(x): + if x <= 0: + raise ValueError(f"{x} is not positive") diff --git a/lib/common/pylint_data/messages/misplaced-comparison-constant/bad.py b/lib/common/pylint_data/messages/misplaced-comparison-constant/bad.py new file mode 100644 index 0000000..1a5712a --- /dev/null +++ b/lib/common/pylint_data/messages/misplaced-comparison-constant/bad.py @@ -0,0 +1,8 @@ +def compare_apples(apples=20): + for i in range(10): + if 5 <= i: # [misplaced-comparison-constant] + pass + if 1 == i: # [misplaced-comparison-constant] + pass + if 20 < len(apples): # [misplaced-comparison-constant] + pass diff --git a/lib/common/pylint_data/messages/misplaced-comparison-constant/good.py b/lib/common/pylint_data/messages/misplaced-comparison-constant/good.py new file mode 100644 index 0000000..ba00a7f --- /dev/null +++ b/lib/common/pylint_data/messages/misplaced-comparison-constant/good.py @@ -0,0 +1,8 @@ +def compare_apples(apples=20): + for i in range(10): + if i >= 5: + pass + if i == 1: + pass + if len(apples) > 20: + pass diff --git a/lib/common/pylint_data/messages/misplaced-format-function/bad.py b/lib/common/pylint_data/messages/misplaced-format-function/bad.py new file mode 100644 index 0000000..bc40bba --- /dev/null +++ b/lib/common/pylint_data/messages/misplaced-format-function/bad.py @@ -0,0 +1 @@ +print("Value: {}").format("Car") # [misplaced-format-function] diff --git a/lib/common/pylint_data/messages/misplaced-format-function/good.py b/lib/common/pylint_data/messages/misplaced-format-function/good.py new file mode 100644 index 0000000..8b31d5c --- /dev/null +++ b/lib/common/pylint_data/messages/misplaced-format-function/good.py @@ -0,0 +1 @@ +print("Value: {}".format("Car")) diff --git a/lib/common/pylint_data/messages/misplaced-future/bad.py b/lib/common/pylint_data/messages/misplaced-future/bad.py new file mode 100644 index 0000000..1fa872f --- /dev/null +++ b/lib/common/pylint_data/messages/misplaced-future/bad.py @@ -0,0 +1,3 @@ +import sys + +from __future__ import print_function # [misplaced-future] diff --git a/lib/common/pylint_data/messages/misplaced-future/details.md b/lib/common/pylint_data/messages/misplaced-future/details.md new file mode 100644 index 0000000..081c8f0 --- /dev/null +++ b/lib/common/pylint_data/messages/misplaced-future/details.md @@ -0,0 +1,3 @@ +A bare raise statement will re-raise the last active exception in the +current scope. If the `raise` statement is not in an `except` or +`finally` block, a RuntimeError will be raised instead. diff --git a/lib/common/pylint_data/messages/misplaced-future/good.py b/lib/common/pylint_data/messages/misplaced-future/good.py new file mode 100644 index 0000000..0124db5 --- /dev/null +++ b/lib/common/pylint_data/messages/misplaced-future/good.py @@ -0,0 +1,3 @@ +from __future__ import print_function + +import sys diff --git a/lib/common/pylint_data/messages/missing-any-param-doc/bad.py b/lib/common/pylint_data/messages/missing-any-param-doc/bad.py new file mode 100644 index 0000000..bf5f232 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-any-param-doc/bad.py @@ -0,0 +1,3 @@ +def puppies(head, tail): # [missing-any-param-doc] + """Print puppy's details.""" + print(head, tail) diff --git a/lib/common/pylint_data/messages/missing-any-param-doc/good.py b/lib/common/pylint_data/messages/missing-any-param-doc/good.py new file mode 100644 index 0000000..6fa9622 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-any-param-doc/good.py @@ -0,0 +1,7 @@ +def puppies(head: str, tail: str): + """Print puppy's details. + + :param head: description of the head of the dog + :param tail: description of the tail of the dog + """ + print(head, tail) diff --git a/lib/common/pylint_data/messages/missing-class-docstring/bad.py b/lib/common/pylint_data/messages/missing-class-docstring/bad.py new file mode 100644 index 0000000..e3e1ddb --- /dev/null +++ b/lib/common/pylint_data/messages/missing-class-docstring/bad.py @@ -0,0 +1,4 @@ +class Person: # [missing-class-docstring] + def __init__(self, first_name, last_name): + self.first_name = first_name + self.last_name = last_name diff --git a/lib/common/pylint_data/messages/missing-class-docstring/good.py b/lib/common/pylint_data/messages/missing-class-docstring/good.py new file mode 100644 index 0000000..72287cf --- /dev/null +++ b/lib/common/pylint_data/messages/missing-class-docstring/good.py @@ -0,0 +1,6 @@ +class Person: + """Class representing a person""" + + def __init__(self, first_name, last_name): + self.first_name = first_name + self.last_name = last_name diff --git a/lib/common/pylint_data/messages/missing-final-newline/bad.py b/lib/common/pylint_data/messages/missing-final-newline/bad.py new file mode 100644 index 0000000..672e229 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-final-newline/bad.py @@ -0,0 +1,8 @@ +print("Hello") # CRLF (\r\n) +print("world") # End-of-file (EOF) +# [missing-final-newline] + +print("Hello") # LF (\n) +print("world") # End-of-file (EOF) +# [missing-final-newline] + diff --git a/lib/common/pylint_data/messages/missing-final-newline/details.md b/lib/common/pylint_data/messages/missing-final-newline/details.md new file mode 100644 index 0000000..fa7958c --- /dev/null +++ b/lib/common/pylint_data/messages/missing-final-newline/details.md @@ -0,0 +1,4 @@ +The POSIX standard defines a line as: + +: \"A sequence of zero or more non- \ characters plus a + terminating \ character.\" diff --git a/lib/common/pylint_data/messages/missing-final-newline/good.py b/lib/common/pylint_data/messages/missing-final-newline/good.py new file mode 100644 index 0000000..c0a1035 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-final-newline/good.py @@ -0,0 +1,7 @@ +print("Hello") # CRLF (\r\n) +print("world") # CRLF (\r\n) +# End-of-file (EOF) + +print("Hello") # LF (\n) +print("world") # LF (\n) +# End-of-file (EOF) diff --git a/lib/common/pylint_data/messages/missing-final-newline/related.md b/lib/common/pylint_data/messages/missing-final-newline/related.md new file mode 100644 index 0000000..7460f36 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-final-newline/related.md @@ -0,0 +1,3 @@ +- [POSIX Standard](https://pubs.opengroup.org/onlinepubs/9699919799/) +- [POSIX Standard Chapter 3.206 + Line](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206) diff --git a/lib/common/pylint_data/messages/missing-format-argument-key/bad.py b/lib/common/pylint_data/messages/missing-format-argument-key/bad.py new file mode 100644 index 0000000..152c393 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-format-argument-key/bad.py @@ -0,0 +1 @@ +print("My name is {first} {last}".format(first="John")) # [missing-format-argument-key] diff --git a/lib/common/pylint_data/messages/missing-format-argument-key/good.py b/lib/common/pylint_data/messages/missing-format-argument-key/good.py new file mode 100644 index 0000000..6570af1 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-format-argument-key/good.py @@ -0,0 +1 @@ +print("My name is {first} {last}".format(first="John", last="Wick")) diff --git a/lib/common/pylint_data/messages/missing-format-argument-key/related.md b/lib/common/pylint_data/messages/missing-format-argument-key/related.md new file mode 100644 index 0000000..6d3196a --- /dev/null +++ b/lib/common/pylint_data/messages/missing-format-argument-key/related.md @@ -0,0 +1,3 @@ +- [PEP 3101](https://peps.python.org/pep-3101/) +- [Custom String + Formatting](https://docs.python.org/3/library/string.html#custom-string-formatting) diff --git a/lib/common/pylint_data/messages/missing-format-attribute/bad.py b/lib/common/pylint_data/messages/missing-format-attribute/bad.py new file mode 100644 index 0000000..2144c7d --- /dev/null +++ b/lib/common/pylint_data/messages/missing-format-attribute/bad.py @@ -0,0 +1 @@ +print("{0.real}".format("1")) # [missing-format-attribute] diff --git a/lib/common/pylint_data/messages/missing-format-attribute/good.py b/lib/common/pylint_data/messages/missing-format-attribute/good.py new file mode 100644 index 0000000..71f89a0 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-format-attribute/good.py @@ -0,0 +1 @@ +print("{0.real}".format(1)) diff --git a/lib/common/pylint_data/messages/missing-format-string-key/bad.py b/lib/common/pylint_data/messages/missing-format-string-key/bad.py new file mode 100644 index 0000000..87c0557 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-format-string-key/bad.py @@ -0,0 +1,5 @@ +# +1: [missing-format-string-key] +fruit_prices = """ +Apple: %(apple_price)d ¤ +Orange: %(orange_price)d ¤ +""" % {"apple_price": 42} diff --git a/lib/common/pylint_data/messages/missing-format-string-key/good.py b/lib/common/pylint_data/messages/missing-format-string-key/good.py new file mode 100644 index 0000000..11b8567 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-format-string-key/good.py @@ -0,0 +1,7 @@ +fruit_prices = """ +Apple: %(apple_price)d ¤ +Orange: %(orange_price)d ¤ +""" % { + "apple_price": 42, + "orange_price": 87, +} diff --git a/lib/common/pylint_data/messages/missing-function-docstring/bad.py b/lib/common/pylint_data/messages/missing-function-docstring/bad.py new file mode 100644 index 0000000..9c54dfc --- /dev/null +++ b/lib/common/pylint_data/messages/missing-function-docstring/bad.py @@ -0,0 +1,5 @@ +import sys + + +def print_python_version(): # [missing-function-docstring] + print(sys.version) diff --git a/lib/common/pylint_data/messages/missing-function-docstring/good.py b/lib/common/pylint_data/messages/missing-function-docstring/good.py new file mode 100644 index 0000000..1942167 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-function-docstring/good.py @@ -0,0 +1,6 @@ +import sys + + +def print_python_version(): + """Function printing python version.""" + print(sys.version) diff --git a/lib/common/pylint_data/messages/missing-kwoa/bad.py b/lib/common/pylint_data/messages/missing-kwoa/bad.py new file mode 100644 index 0000000..2b8deab --- /dev/null +++ b/lib/common/pylint_data/messages/missing-kwoa/bad.py @@ -0,0 +1,6 @@ +def target(pos, *, keyword): + return pos + keyword + + +def not_forwarding_kwargs(*args, **kwargs): + target(*args) # [missing-kwoa] diff --git a/lib/common/pylint_data/messages/missing-kwoa/good.py b/lib/common/pylint_data/messages/missing-kwoa/good.py new file mode 100644 index 0000000..c212deb --- /dev/null +++ b/lib/common/pylint_data/messages/missing-kwoa/good.py @@ -0,0 +1,6 @@ +def target(pos, *, keyword): + return pos + keyword + + +def not_forwarding_kwargs(*args, **kwargs): + target(*args, **kwargs) diff --git a/lib/common/pylint_data/messages/missing-module-docstring/bad.py b/lib/common/pylint_data/messages/missing-module-docstring/bad.py new file mode 100644 index 0000000..7f7d588 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-module-docstring/bad.py @@ -0,0 +1,5 @@ +import sys # [missing-module-docstring] + + +def print_python_version(): + print(sys.version) diff --git a/lib/common/pylint_data/messages/missing-module-docstring/good.py b/lib/common/pylint_data/messages/missing-module-docstring/good.py new file mode 100644 index 0000000..1229bbd --- /dev/null +++ b/lib/common/pylint_data/messages/missing-module-docstring/good.py @@ -0,0 +1,7 @@ +"""Module providing a function printing python version.""" + +import sys + + +def print_python_version(): + print(sys.version) diff --git a/lib/common/pylint_data/messages/missing-param-doc/bad.py b/lib/common/pylint_data/messages/missing-param-doc/bad.py new file mode 100644 index 0000000..e3d5166 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-param-doc/bad.py @@ -0,0 +1,5 @@ +def integer_sum(a: int, b): # [missing-param-doc] + """Returns sum of two integers + :param a: first integer + """ + return a + b diff --git a/lib/common/pylint_data/messages/missing-param-doc/good.py b/lib/common/pylint_data/messages/missing-param-doc/good.py new file mode 100644 index 0000000..b2885dd --- /dev/null +++ b/lib/common/pylint_data/messages/missing-param-doc/good.py @@ -0,0 +1,6 @@ +def integer_sum(a: int, b: int): + """Returns sum of two integers + :param a: first integer + :param b: second integer + """ + return a + b diff --git a/lib/common/pylint_data/messages/missing-parentheses-for-call-in-test/bad.py b/lib/common/pylint_data/messages/missing-parentheses-for-call-in-test/bad.py new file mode 100644 index 0000000..e4a7172 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-parentheses-for-call-in-test/bad.py @@ -0,0 +1,9 @@ +import random + + +def is_it_a_good_day(): + return random.choice([True, False]) + + +if is_it_a_good_day: # [missing-parentheses-for-call-in-test] + print("Today is a good day!") diff --git a/lib/common/pylint_data/messages/missing-parentheses-for-call-in-test/good.py b/lib/common/pylint_data/messages/missing-parentheses-for-call-in-test/good.py new file mode 100644 index 0000000..80afd77 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-parentheses-for-call-in-test/good.py @@ -0,0 +1,9 @@ +import random + + +def is_it_a_good_day(): + return random.choice([True, False]) + + +if is_it_a_good_day(): + print("Today is a good day!") diff --git a/lib/common/pylint_data/messages/missing-raises-doc/bad.py b/lib/common/pylint_data/messages/missing-raises-doc/bad.py new file mode 100644 index 0000000..51efd39 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-raises-doc/bad.py @@ -0,0 +1,8 @@ +def integer_sum(a: int, b: int): # [missing-raises-doc] + """Returns sum of two integers + :param a: first integer + :param b: second integer + """ + if not (isinstance(a, int) and isinstance(b, int)): + raise ValueError("Function supports only integer parameters.") + return a + b diff --git a/lib/common/pylint_data/messages/missing-raises-doc/good.py b/lib/common/pylint_data/messages/missing-raises-doc/good.py new file mode 100644 index 0000000..6ca44d0 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-raises-doc/good.py @@ -0,0 +1,9 @@ +def integer_sum(a: int, b: int): + """Returns sum of two integers + :param a: first integer + :param b: second integer + :raises ValueError: One of the parameters is not an integer. + """ + if not (isinstance(a, int) and isinstance(b, int)): + raise ValueError("Function supports only integer parameters.") + return a + b diff --git a/lib/common/pylint_data/messages/missing-return-doc/bad.py b/lib/common/pylint_data/messages/missing-return-doc/bad.py new file mode 100644 index 0000000..904c267 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-return-doc/bad.py @@ -0,0 +1,6 @@ +def integer_sum(a: int, b: int): # [missing-return-doc] + """Returns sum of two integers + :param a: first integer + :param b: second integer + """ + return a + b diff --git a/lib/common/pylint_data/messages/missing-return-doc/details.md b/lib/common/pylint_data/messages/missing-return-doc/details.md new file mode 100644 index 0000000..c11ff2e --- /dev/null +++ b/lib/common/pylint_data/messages/missing-return-doc/details.md @@ -0,0 +1,2 @@ +This message is raised only when parameter `accept-no-return-doc` is set +to `no`. diff --git a/lib/common/pylint_data/messages/missing-return-doc/good.py b/lib/common/pylint_data/messages/missing-return-doc/good.py new file mode 100644 index 0000000..28dd62a --- /dev/null +++ b/lib/common/pylint_data/messages/missing-return-doc/good.py @@ -0,0 +1,7 @@ +def integer_sum(a: int, b: int) -> int: + """Returns sum of two integers + :param a: first integer + :param b: second integer + :return: sum of parameters a and b + """ + return a + b diff --git a/lib/common/pylint_data/messages/missing-return-type-doc/bad.py b/lib/common/pylint_data/messages/missing-return-type-doc/bad.py new file mode 100644 index 0000000..d2d9502 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-return-type-doc/bad.py @@ -0,0 +1,7 @@ +def integer_sum(a: int, b: int): # [missing-return-type-doc] + """Returns sum of two integers + :param a: first integer + :param b: second integer + :return: sum of parameters a and b + """ + return a + b diff --git a/lib/common/pylint_data/messages/missing-return-type-doc/details.md b/lib/common/pylint_data/messages/missing-return-type-doc/details.md new file mode 100644 index 0000000..c11ff2e --- /dev/null +++ b/lib/common/pylint_data/messages/missing-return-type-doc/details.md @@ -0,0 +1,2 @@ +This message is raised only when parameter `accept-no-return-doc` is set +to `no`. diff --git a/lib/common/pylint_data/messages/missing-return-type-doc/good.py b/lib/common/pylint_data/messages/missing-return-type-doc/good.py new file mode 100644 index 0000000..28dd62a --- /dev/null +++ b/lib/common/pylint_data/messages/missing-return-type-doc/good.py @@ -0,0 +1,7 @@ +def integer_sum(a: int, b: int) -> int: + """Returns sum of two integers + :param a: first integer + :param b: second integer + :return: sum of parameters a and b + """ + return a + b diff --git a/lib/common/pylint_data/messages/missing-timeout/bad.py b/lib/common/pylint_data/messages/missing-timeout/bad.py new file mode 100644 index 0000000..52444f2 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-timeout/bad.py @@ -0,0 +1,3 @@ +import requests + +requests.post("http://localhost") # [missing-timeout] diff --git a/lib/common/pylint_data/messages/missing-timeout/details.md b/lib/common/pylint_data/messages/missing-timeout/details.md new file mode 100644 index 0000000..e9c2c74 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-timeout/details.md @@ -0,0 +1,11 @@ +You can add new methods that should have a defined `` `timeout `` +argument as qualified names in the `timeout-methods` option, for +example: + +- `requests.api.get` +- `requests.api.head` +- `requests.api.options` +- `requests.api.patch` +- `requests.api.post` +- `requests.api.put` +- `requests.api.request` diff --git a/lib/common/pylint_data/messages/missing-timeout/good.py b/lib/common/pylint_data/messages/missing-timeout/good.py new file mode 100644 index 0000000..dbeb512 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-timeout/good.py @@ -0,0 +1,3 @@ +import requests + +requests.post("http://localhost", timeout=10) diff --git a/lib/common/pylint_data/messages/missing-type-doc/bad.py b/lib/common/pylint_data/messages/missing-type-doc/bad.py new file mode 100644 index 0000000..e5ba9e9 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-type-doc/bad.py @@ -0,0 +1,6 @@ +def integer_sum(a: int, b): # [missing-type-doc] + """Returns sum of two integers + :param a: first integer + :param b: second integer + """ + return a + b diff --git a/lib/common/pylint_data/messages/missing-type-doc/good.py b/lib/common/pylint_data/messages/missing-type-doc/good.py new file mode 100644 index 0000000..b2885dd --- /dev/null +++ b/lib/common/pylint_data/messages/missing-type-doc/good.py @@ -0,0 +1,6 @@ +def integer_sum(a: int, b: int): + """Returns sum of two integers + :param a: first integer + :param b: second integer + """ + return a + b diff --git a/lib/common/pylint_data/messages/missing-yield-doc/bad.py b/lib/common/pylint_data/messages/missing-yield-doc/bad.py new file mode 100644 index 0000000..63f9eb2 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-yield-doc/bad.py @@ -0,0 +1,9 @@ +def even_number_under(n: int): # [missing-yield-doc] + """Prints even numbers smaller than n. + Args: + n: Upper limit of even numbers. + """ + for i in range(n): + if i % 2 == 1: + continue + yield i diff --git a/lib/common/pylint_data/messages/missing-yield-doc/details.md b/lib/common/pylint_data/messages/missing-yield-doc/details.md new file mode 100644 index 0000000..0936c0f --- /dev/null +++ b/lib/common/pylint_data/messages/missing-yield-doc/details.md @@ -0,0 +1,2 @@ +This message is raised only when parameter `accept-no-yields-doc` is set +to `no`. diff --git a/lib/common/pylint_data/messages/missing-yield-doc/good.py b/lib/common/pylint_data/messages/missing-yield-doc/good.py new file mode 100644 index 0000000..f9cb9fd --- /dev/null +++ b/lib/common/pylint_data/messages/missing-yield-doc/good.py @@ -0,0 +1,15 @@ +from typing import Iterator + + +def even_number_under(n: int) -> Iterator[int]: + """Prints even numbers smaller than n. + Args: + n: Upper limit of even numbers. + + Yields: + even numbers + """ + for i in range(n): + if i % 2 == 1: + continue + yield i diff --git a/lib/common/pylint_data/messages/missing-yield-type-doc/bad.py b/lib/common/pylint_data/messages/missing-yield-type-doc/bad.py new file mode 100644 index 0000000..a417275 --- /dev/null +++ b/lib/common/pylint_data/messages/missing-yield-type-doc/bad.py @@ -0,0 +1,12 @@ +def even_number_under(n: int): # [missing-yield-type-doc] + """Prints even numbers smaller than n. + Args: + n: Upper limit of even numbers. + + Yields: + even numbers + """ + for i in range(n): + if i % 2 == 1: + continue + yield i diff --git a/lib/common/pylint_data/messages/missing-yield-type-doc/details.md b/lib/common/pylint_data/messages/missing-yield-type-doc/details.md new file mode 100644 index 0000000..0936c0f --- /dev/null +++ b/lib/common/pylint_data/messages/missing-yield-type-doc/details.md @@ -0,0 +1,2 @@ +This message is raised only when parameter `accept-no-yields-doc` is set +to `no`. diff --git a/lib/common/pylint_data/messages/missing-yield-type-doc/good.py b/lib/common/pylint_data/messages/missing-yield-type-doc/good.py new file mode 100644 index 0000000..f9cb9fd --- /dev/null +++ b/lib/common/pylint_data/messages/missing-yield-type-doc/good.py @@ -0,0 +1,15 @@ +from typing import Iterator + + +def even_number_under(n: int) -> Iterator[int]: + """Prints even numbers smaller than n. + Args: + n: Upper limit of even numbers. + + Yields: + even numbers + """ + for i in range(n): + if i % 2 == 1: + continue + yield i diff --git a/lib/common/pylint_data/messages/mixed-format-string/bad.py b/lib/common/pylint_data/messages/mixed-format-string/bad.py new file mode 100644 index 0000000..b0b455e --- /dev/null +++ b/lib/common/pylint_data/messages/mixed-format-string/bad.py @@ -0,0 +1 @@ +print("x=%(x)d, y=%d" % (0, 1)) # [mixed-format-string] diff --git a/lib/common/pylint_data/messages/mixed-format-string/good.py b/lib/common/pylint_data/messages/mixed-format-string/good.py new file mode 100644 index 0000000..f640256 --- /dev/null +++ b/lib/common/pylint_data/messages/mixed-format-string/good.py @@ -0,0 +1,5 @@ +# Only named +print("x=%(x)d, y=%(y)d" % {"x": 0, "y": 1}) + +# Only ordered +print("x=%d, y=%d" % (0, 1)) \ No newline at end of file diff --git a/lib/common/pylint_data/messages/mixed-line-endings/bad.py b/lib/common/pylint_data/messages/mixed-line-endings/bad.py new file mode 100644 index 0000000..5e6138d --- /dev/null +++ b/lib/common/pylint_data/messages/mixed-line-endings/bad.py @@ -0,0 +1,2 @@ +print("Hello") # CRLF (\r\n) +print("World") # LF (\n) # [mixed-line-endings] diff --git a/lib/common/pylint_data/messages/mixed-line-endings/good.py b/lib/common/pylint_data/messages/mixed-line-endings/good.py new file mode 100644 index 0000000..614b9c0 --- /dev/null +++ b/lib/common/pylint_data/messages/mixed-line-endings/good.py @@ -0,0 +1,5 @@ +print("Hello") # CRLF (\r\n) +print("World") # CRLF (\r\n) + +print("Hello") # LF (\n) +print("World") # LF (\n) diff --git a/lib/common/pylint_data/messages/mixed-line-endings/related.md b/lib/common/pylint_data/messages/mixed-line-endings/related.md new file mode 100644 index 0000000..89faf54 --- /dev/null +++ b/lib/common/pylint_data/messages/mixed-line-endings/related.md @@ -0,0 +1,5 @@ +- [History of CRLF and LF](https://stackoverflow.com/a/6521730/2519059) +- [Dealing with line endings in + Git](https://stackoverflow.com/a/10855862/2519059) +- [A Collection of Useful .gitattributes + Templates](https://github.com/alexkaratarakis/gitattributes) diff --git a/lib/common/pylint_data/messages/modified-iterating-dict/bad.py b/lib/common/pylint_data/messages/modified-iterating-dict/bad.py new file mode 100644 index 0000000..cd31a62 --- /dev/null +++ b/lib/common/pylint_data/messages/modified-iterating-dict/bad.py @@ -0,0 +1,6 @@ +fruits = {"apple": 1, "orange": 2, "mango": 3} + +i = 0 +for fruit in fruits: + fruits["apple"] = i # [modified-iterating-dict] + i += 1 diff --git a/lib/common/pylint_data/messages/modified-iterating-dict/good.py b/lib/common/pylint_data/messages/modified-iterating-dict/good.py new file mode 100644 index 0000000..8755a6c --- /dev/null +++ b/lib/common/pylint_data/messages/modified-iterating-dict/good.py @@ -0,0 +1,6 @@ +fruits = {"apple": 1, "orange": 2, "mango": 3} + +i = 0 +for fruit in fruits.copy(): + fruits["apple"] = i + i += 1 diff --git a/lib/common/pylint_data/messages/modified-iterating-list/bad.py b/lib/common/pylint_data/messages/modified-iterating-list/bad.py new file mode 100644 index 0000000..57d7715 --- /dev/null +++ b/lib/common/pylint_data/messages/modified-iterating-list/bad.py @@ -0,0 +1,3 @@ +fruits = ["apple", "orange", "mango"] +for fruit in fruits: + fruits.append("pineapple") # [modified-iterating-list] diff --git a/lib/common/pylint_data/messages/modified-iterating-list/good.py b/lib/common/pylint_data/messages/modified-iterating-list/good.py new file mode 100644 index 0000000..0132f8b --- /dev/null +++ b/lib/common/pylint_data/messages/modified-iterating-list/good.py @@ -0,0 +1,3 @@ +fruits = ["apple", "orange", "mango"] +for fruit in fruits.copy(): + fruits.append("pineapple") diff --git a/lib/common/pylint_data/messages/modified-iterating-set/bad.py b/lib/common/pylint_data/messages/modified-iterating-set/bad.py new file mode 100644 index 0000000..bd82a56 --- /dev/null +++ b/lib/common/pylint_data/messages/modified-iterating-set/bad.py @@ -0,0 +1,3 @@ +fruits = {"apple", "orange", "mango"} +for fruit in fruits: + fruits.add(fruit + "yum") # [modified-iterating-set] diff --git a/lib/common/pylint_data/messages/modified-iterating-set/good.py b/lib/common/pylint_data/messages/modified-iterating-set/good.py new file mode 100644 index 0000000..0af8e24 --- /dev/null +++ b/lib/common/pylint_data/messages/modified-iterating-set/good.py @@ -0,0 +1,3 @@ +fruits = {"apple", "orange", "mango"} +for fruit in fruits.copy(): + fruits.add(fruit + "yum") diff --git a/lib/common/pylint_data/messages/multiple-class-sub-patterns/bad.py b/lib/common/pylint_data/messages/multiple-class-sub-patterns/bad.py new file mode 100644 index 0000000..0896381 --- /dev/null +++ b/lib/common/pylint_data/messages/multiple-class-sub-patterns/bad.py @@ -0,0 +1,14 @@ +class Book: + __match_args__ = ("title", "year") + + def __init__(self, title, year): + self.title = title + self.year = year + + +def func(item: Book): + match item: + case Book("abc", title="abc"): # [multiple-class-sub-patterns] + ... + case Book(year=2000, year=2001): # [multiple-class-sub-patterns] + ... diff --git a/lib/common/pylint_data/messages/multiple-class-sub-patterns/good.py b/lib/common/pylint_data/messages/multiple-class-sub-patterns/good.py new file mode 100644 index 0000000..8791878 --- /dev/null +++ b/lib/common/pylint_data/messages/multiple-class-sub-patterns/good.py @@ -0,0 +1,14 @@ +class Book: + __match_args__ = ("title", "year") + + def __init__(self, title, year): + self.title = title + self.year = year + + +def func(item: Book): + match item: + case Book(title="abc"): + ... + case Book(year=2000): + ... diff --git a/lib/common/pylint_data/messages/multiple-class-sub-patterns/related.md b/lib/common/pylint_data/messages/multiple-class-sub-patterns/related.md new file mode 100644 index 0000000..6c207f8 --- /dev/null +++ b/lib/common/pylint_data/messages/multiple-class-sub-patterns/related.md @@ -0,0 +1,2 @@ +- [Python + documentation](https://docs.python.org/3/reference/compound_stmts.html#class-patterns) diff --git a/lib/common/pylint_data/messages/multiple-constructor-doc/bad.py b/lib/common/pylint_data/messages/multiple-constructor-doc/bad.py new file mode 100644 index 0000000..44defd1 --- /dev/null +++ b/lib/common/pylint_data/messages/multiple-constructor-doc/bad.py @@ -0,0 +1,15 @@ +class Point: # [multiple-constructor-doc] + """Represents a point in the xy-coordinate plane. + + :param x: coordinate + :param y: coordinate + """ + + def __init__(self, x, y): + """Represents a point in the xy-coordinate plane. + + :param x: coordinate + :param y: coordinate + """ + self.x = x + self.y = y diff --git a/lib/common/pylint_data/messages/multiple-constructor-doc/details.md b/lib/common/pylint_data/messages/multiple-constructor-doc/details.md new file mode 100644 index 0000000..3639ec3 --- /dev/null +++ b/lib/common/pylint_data/messages/multiple-constructor-doc/details.md @@ -0,0 +1 @@ +Both docstrings are acceptable but not both at the same time. diff --git a/lib/common/pylint_data/messages/multiple-constructor-doc/good.py b/lib/common/pylint_data/messages/multiple-constructor-doc/good.py new file mode 100644 index 0000000..d96d5ce --- /dev/null +++ b/lib/common/pylint_data/messages/multiple-constructor-doc/good.py @@ -0,0 +1,9 @@ +class Point: + def __init__(self, x, y): + """Represents a point in the xy-coordinate plane. + + :param x: x coordinate + :param y: y coordinate + """ + self.x = x + self.y = y diff --git a/lib/common/pylint_data/messages/multiple-imports/bad.py b/lib/common/pylint_data/messages/multiple-imports/bad.py new file mode 100644 index 0000000..21a990d --- /dev/null +++ b/lib/common/pylint_data/messages/multiple-imports/bad.py @@ -0,0 +1 @@ +import os, sys # [multiple-imports] diff --git a/lib/common/pylint_data/messages/multiple-imports/good.py b/lib/common/pylint_data/messages/multiple-imports/good.py new file mode 100644 index 0000000..82cda0b --- /dev/null +++ b/lib/common/pylint_data/messages/multiple-imports/good.py @@ -0,0 +1,2 @@ +import os +import sys diff --git a/lib/common/pylint_data/messages/multiple-statements/bad.py b/lib/common/pylint_data/messages/multiple-statements/bad.py new file mode 100644 index 0000000..754eede --- /dev/null +++ b/lib/common/pylint_data/messages/multiple-statements/bad.py @@ -0,0 +1,5 @@ +fruits = ["apple", "orange", "mango"] + +if "apple" in fruits: pass # [multiple-statements] +else: + print("no apples!") diff --git a/lib/common/pylint_data/messages/multiple-statements/good.py b/lib/common/pylint_data/messages/multiple-statements/good.py new file mode 100644 index 0000000..9328c83 --- /dev/null +++ b/lib/common/pylint_data/messages/multiple-statements/good.py @@ -0,0 +1,6 @@ +fruits = ["apple", "orange", "mango"] + +if "apple" in fruits: + pass +else: + print("no apples!") diff --git a/lib/common/pylint_data/messages/named-expr-without-context/bad.py b/lib/common/pylint_data/messages/named-expr-without-context/bad.py new file mode 100644 index 0000000..c5d2ffb --- /dev/null +++ b/lib/common/pylint_data/messages/named-expr-without-context/bad.py @@ -0,0 +1 @@ +(a := 42) # [named-expr-without-context] diff --git a/lib/common/pylint_data/messages/named-expr-without-context/good.py b/lib/common/pylint_data/messages/named-expr-without-context/good.py new file mode 100644 index 0000000..fb088bb --- /dev/null +++ b/lib/common/pylint_data/messages/named-expr-without-context/good.py @@ -0,0 +1,2 @@ +if a := 42: + print("Success") diff --git a/lib/common/pylint_data/messages/nan-comparison/bad.py b/lib/common/pylint_data/messages/nan-comparison/bad.py new file mode 100644 index 0000000..9116865 --- /dev/null +++ b/lib/common/pylint_data/messages/nan-comparison/bad.py @@ -0,0 +1,5 @@ +import numpy as np + + +def both_nan(x, y) -> bool: + return x == np.NaN and y == float("nan") # [nan-comparison, nan-comparison] diff --git a/lib/common/pylint_data/messages/nan-comparison/good.py b/lib/common/pylint_data/messages/nan-comparison/good.py new file mode 100644 index 0000000..31f54ed --- /dev/null +++ b/lib/common/pylint_data/messages/nan-comparison/good.py @@ -0,0 +1,5 @@ +import numpy as np + + +def both_nan(x, y) -> bool: + return np.isnan(x) and np.isnan(y) diff --git a/lib/common/pylint_data/messages/nested-min-max/bad.py b/lib/common/pylint_data/messages/nested-min-max/bad.py new file mode 100644 index 0000000..b3e13db --- /dev/null +++ b/lib/common/pylint_data/messages/nested-min-max/bad.py @@ -0,0 +1 @@ +print(min(1, min(2, 3))) # [nested-min-max] diff --git a/lib/common/pylint_data/messages/nested-min-max/good.py b/lib/common/pylint_data/messages/nested-min-max/good.py new file mode 100644 index 0000000..2d348b2 --- /dev/null +++ b/lib/common/pylint_data/messages/nested-min-max/good.py @@ -0,0 +1 @@ +print(min(1, 2, 3)) diff --git a/lib/common/pylint_data/messages/no-classmethod-decorator/bad.py b/lib/common/pylint_data/messages/no-classmethod-decorator/bad.py new file mode 100644 index 0000000..55c4f4d --- /dev/null +++ b/lib/common/pylint_data/messages/no-classmethod-decorator/bad.py @@ -0,0 +1,11 @@ +class Fruit: + COLORS = [] + + def __init__(self, color): + self.color = color + + def pick_colors(cls, *args): + """classmethod to pick fruit colors""" + cls.COLORS = args + + pick_colors = classmethod(pick_colors) # [no-classmethod-decorator] diff --git a/lib/common/pylint_data/messages/no-classmethod-decorator/good.py b/lib/common/pylint_data/messages/no-classmethod-decorator/good.py new file mode 100644 index 0000000..9b70c76 --- /dev/null +++ b/lib/common/pylint_data/messages/no-classmethod-decorator/good.py @@ -0,0 +1,10 @@ +class Fruit: + COLORS = [] + + def __init__(self, color): + self.color = color + + @classmethod + def pick_colors(cls, *args): + """classmethod to pick fruit colors""" + cls.COLORS = args diff --git a/lib/common/pylint_data/messages/no-else-break/bad.py b/lib/common/pylint_data/messages/no-else-break/bad.py new file mode 100644 index 0000000..1785562 --- /dev/null +++ b/lib/common/pylint_data/messages/no-else-break/bad.py @@ -0,0 +1,6 @@ +def next_seven_elements(iterator): + for i, item in enumerate(iterator): + if i == 7: # [no-else-break] + break + else: + yield item diff --git a/lib/common/pylint_data/messages/no-else-break/good.py b/lib/common/pylint_data/messages/no-else-break/good.py new file mode 100644 index 0000000..414a0e6 --- /dev/null +++ b/lib/common/pylint_data/messages/no-else-break/good.py @@ -0,0 +1,5 @@ +def next_seven_elements(iterator): + for i, item in enumerate(iterator): + if i == 7: + break + yield item diff --git a/lib/common/pylint_data/messages/no-else-continue/bad.py b/lib/common/pylint_data/messages/no-else-continue/bad.py new file mode 100644 index 0000000..a561f93 --- /dev/null +++ b/lib/common/pylint_data/messages/no-else-continue/bad.py @@ -0,0 +1,6 @@ +def even_number_under(n: int): + for i in range(n): + if i % 2 == 1: # [no-else-continue] + continue + else: + yield i diff --git a/lib/common/pylint_data/messages/no-else-continue/good.py b/lib/common/pylint_data/messages/no-else-continue/good.py new file mode 100644 index 0000000..e73e0e2 --- /dev/null +++ b/lib/common/pylint_data/messages/no-else-continue/good.py @@ -0,0 +1,5 @@ +def even_number_under(n: int): + for i in range(n): + if i % 2 == 1: + continue + yield i diff --git a/lib/common/pylint_data/messages/no-else-raise/bad.py b/lib/common/pylint_data/messages/no-else-raise/bad.py new file mode 100644 index 0000000..c5fdf99 --- /dev/null +++ b/lib/common/pylint_data/messages/no-else-raise/bad.py @@ -0,0 +1,5 @@ +def integer_sum(a: int, b: int) -> int: + if not (isinstance(a, int) and isinstance(b, int)): # [no-else-raise] + raise ValueError("Function supports only integer parameters.") + else: + return a + b diff --git a/lib/common/pylint_data/messages/no-else-raise/good.py b/lib/common/pylint_data/messages/no-else-raise/good.py new file mode 100644 index 0000000..bf29181 --- /dev/null +++ b/lib/common/pylint_data/messages/no-else-raise/good.py @@ -0,0 +1,4 @@ +def integer_sum(a: int, b: int) -> int: + if not (isinstance(a, int) and isinstance(b, int)): + raise ValueError("Function supports only integer parameters.") + return a + b diff --git a/lib/common/pylint_data/messages/no-else-return/bad.py b/lib/common/pylint_data/messages/no-else-return/bad.py new file mode 100644 index 0000000..99edf5f --- /dev/null +++ b/lib/common/pylint_data/messages/no-else-return/bad.py @@ -0,0 +1,7 @@ +def compare_numbers(a: int, b: int) -> int: + if a == b: # [no-else-return] + return 0 + elif a < b: + return -1 + else: + return 1 diff --git a/lib/common/pylint_data/messages/no-else-return/good.py b/lib/common/pylint_data/messages/no-else-return/good.py new file mode 100644 index 0000000..b8b4570 --- /dev/null +++ b/lib/common/pylint_data/messages/no-else-return/good.py @@ -0,0 +1,6 @@ +def compare_numbers(a: int, b: int) -> int: + if a == b: + return 0 + if a < b: + return -1 + return 1 diff --git a/lib/common/pylint_data/messages/no-else-return/related.md b/lib/common/pylint_data/messages/no-else-return/related.md new file mode 100644 index 0000000..d28dbcd --- /dev/null +++ b/lib/common/pylint_data/messages/no-else-return/related.md @@ -0,0 +1 @@ +- [Unnecessary-else-statements](https://www.pythonmorsels.com/unnecessary-else-statements/) diff --git a/lib/common/pylint_data/messages/no-member/bad.py b/lib/common/pylint_data/messages/no-member/bad.py new file mode 100644 index 0000000..110ee45 --- /dev/null +++ b/lib/common/pylint_data/messages/no-member/bad.py @@ -0,0 +1,11 @@ +from pathlib import Path + +directories = Path(".").mothers # [no-member] + + +class Cat: + def meow(self): + print("Meow") + + +Cat().roar() # [no-member] diff --git a/lib/common/pylint_data/messages/no-member/details.md b/lib/common/pylint_data/messages/no-member/details.md new file mode 100644 index 0000000..50d97f4 --- /dev/null +++ b/lib/common/pylint_data/messages/no-member/details.md @@ -0,0 +1,43 @@ +If you are getting the dreaded `no-member` error, there is a possibility +that either: + +- pylint found a bug in your code +- You\'re launching pylint without the dependencies installed in its + environment +- pylint would need to lint a C extension module and is refraining to do + so +- pylint does not understand dynamically generated code + +Linting C extension modules is not supported out of the box, especially +since pylint has no way to get an AST object out of the extension +module. + +But pylint actually has a mechanism which you might use in case you want +to analyze C extensions. Pylint has a flag, called +`extension-pkg-allow-list` (formerly `extension-pkg-whitelist`), through +which you can tell it to import that module and to build an AST from +that imported module: + + $ pylint --extension-pkg-allow-list=your_c_extension + +Be aware though that using this flag means that extensions are loaded +into the active Python interpreter and may run arbitrary code, which you +may not want. This is the reason why we disable by default loading C +extensions. In case you do not want the hassle of passing C extensions +module with this flag all the time, you can enable +`unsafe-load-any-extension` in your configuration file, which will build +AST objects from all the C extensions that pylint encounters: + + $ pylint --unsafe-load-any-extension=y + +Alternatively, since pylint emits a separate error for attributes that +cannot be found in C extensions, `c-extension-no-member`, you can +disable this error for your project. + +If something is generated dynamically, pylint won\'t be able to +understand the code from your library (c-extension or not). You can then +specify generated attributes with the `generated-members` option. For +example if `cv2.LINE_AA` and `sphinx.generated_member` create false +positives for `no-member`, you can do: + + $ pylint --generated-member=cv2.LINE_AA,sphinx.generated_member diff --git a/lib/common/pylint_data/messages/no-member/good.py b/lib/common/pylint_data/messages/no-member/good.py new file mode 100644 index 0000000..06fd13d --- /dev/null +++ b/lib/common/pylint_data/messages/no-member/good.py @@ -0,0 +1,11 @@ +from pathlib import Path + +directories = Path(".").parents + + +class Cat: + def meow(self): + print("Meow") + + +Cat().meow() diff --git a/lib/common/pylint_data/messages/no-method-argument/bad.py b/lib/common/pylint_data/messages/no-method-argument/bad.py new file mode 100644 index 0000000..c3a6e67 --- /dev/null +++ b/lib/common/pylint_data/messages/no-method-argument/bad.py @@ -0,0 +1,3 @@ +class Person: + def print_greeting(): # [no-method-argument] + print("hello") diff --git a/lib/common/pylint_data/messages/no-method-argument/good.py b/lib/common/pylint_data/messages/no-method-argument/good.py new file mode 100644 index 0000000..9ab03bb --- /dev/null +++ b/lib/common/pylint_data/messages/no-method-argument/good.py @@ -0,0 +1,3 @@ +class Person: + def print_greeting(self): + print("hello") diff --git a/lib/common/pylint_data/messages/no-name-in-module/bad.py b/lib/common/pylint_data/messages/no-name-in-module/bad.py new file mode 100644 index 0000000..50df863 --- /dev/null +++ b/lib/common/pylint_data/messages/no-name-in-module/bad.py @@ -0,0 +1 @@ +from os import pizza # [no-name-in-module] diff --git a/lib/common/pylint_data/messages/no-name-in-module/good.py b/lib/common/pylint_data/messages/no-name-in-module/good.py new file mode 100644 index 0000000..cfd7c68 --- /dev/null +++ b/lib/common/pylint_data/messages/no-name-in-module/good.py @@ -0,0 +1 @@ +from os import path diff --git a/lib/common/pylint_data/messages/no-self-argument/bad.py b/lib/common/pylint_data/messages/no-self-argument/bad.py new file mode 100644 index 0000000..e195060 --- /dev/null +++ b/lib/common/pylint_data/messages/no-self-argument/bad.py @@ -0,0 +1,3 @@ +class Fruit: + def __init__(this, name): # [no-self-argument] + this.name = name diff --git a/lib/common/pylint_data/messages/no-self-argument/good.py b/lib/common/pylint_data/messages/no-self-argument/good.py new file mode 100644 index 0000000..2e1638a --- /dev/null +++ b/lib/common/pylint_data/messages/no-self-argument/good.py @@ -0,0 +1,3 @@ +class Fruit: + def __init__(self, name): + self.name = name diff --git a/lib/common/pylint_data/messages/no-self-use/bad.py b/lib/common/pylint_data/messages/no-self-use/bad.py new file mode 100644 index 0000000..bf9160a --- /dev/null +++ b/lib/common/pylint_data/messages/no-self-use/bad.py @@ -0,0 +1,3 @@ +class Person: + def greeting(self): # [no-self-use] + print("Greetings pythonista!") diff --git a/lib/common/pylint_data/messages/no-self-use/details.md b/lib/common/pylint_data/messages/no-self-use/details.md new file mode 100644 index 0000000..6840d8b --- /dev/null +++ b/lib/common/pylint_data/messages/no-self-use/details.md @@ -0,0 +1,2 @@ +If a function is not using any class attribute it can be a +`@staticmethod`, or a function outside the class. diff --git a/lib/common/pylint_data/messages/no-self-use/good.py b/lib/common/pylint_data/messages/no-self-use/good.py new file mode 100644 index 0000000..f22a7b5 --- /dev/null +++ b/lib/common/pylint_data/messages/no-self-use/good.py @@ -0,0 +1,17 @@ +# Function +def greeting(): + print("Greetings pythonista!") + + +# Static Method +class Person: + @staticmethod + def greeting(): + print("Greetings pythonista!") + +# Use Self +class Person: + name: str = "Amelia" + + def greeting(self): + print(f"Greetings {self.name} the pythonista!") diff --git a/lib/common/pylint_data/messages/no-staticmethod-decorator/bad.py b/lib/common/pylint_data/messages/no-staticmethod-decorator/bad.py new file mode 100644 index 0000000..181c745 --- /dev/null +++ b/lib/common/pylint_data/messages/no-staticmethod-decorator/bad.py @@ -0,0 +1,5 @@ +class Worm: + def bore(self): + pass + + bore = staticmethod(bore) # [no-staticmethod-decorator] diff --git a/lib/common/pylint_data/messages/no-staticmethod-decorator/good.py b/lib/common/pylint_data/messages/no-staticmethod-decorator/good.py new file mode 100644 index 0000000..d3b8efa --- /dev/null +++ b/lib/common/pylint_data/messages/no-staticmethod-decorator/good.py @@ -0,0 +1,4 @@ +class Worm: + @staticmethod + def bore(self): + pass diff --git a/lib/common/pylint_data/messages/no-value-for-parameter/bad.py b/lib/common/pylint_data/messages/no-value-for-parameter/bad.py new file mode 100644 index 0000000..31b08a1 --- /dev/null +++ b/lib/common/pylint_data/messages/no-value-for-parameter/bad.py @@ -0,0 +1,5 @@ +def add(x, y): + return x + y + + +add(1) # [no-value-for-parameter] diff --git a/lib/common/pylint_data/messages/no-value-for-parameter/good.py b/lib/common/pylint_data/messages/no-value-for-parameter/good.py new file mode 100644 index 0000000..068b525 --- /dev/null +++ b/lib/common/pylint_data/messages/no-value-for-parameter/good.py @@ -0,0 +1,5 @@ +def add(x, y): + return x + y + + +add(1, 2) diff --git a/lib/common/pylint_data/messages/non-ascii-file-name/related.md b/lib/common/pylint_data/messages/non-ascii-file-name/related.md new file mode 100644 index 0000000..e2504af --- /dev/null +++ b/lib/common/pylint_data/messages/non-ascii-file-name/related.md @@ -0,0 +1,3 @@ +- [PEP 489](https://peps.python.org/pep-0489/#export-hook-name) +- [PEP 672](https://peps.python.org/pep-0672/#confusing-features) +- [Python issue 20485](https://bugs.python.org/issue20485) diff --git a/lib/common/pylint_data/messages/non-ascii-module-import/bad.py b/lib/common/pylint_data/messages/non-ascii-module-import/bad.py new file mode 100644 index 0000000..ce2e811 --- /dev/null +++ b/lib/common/pylint_data/messages/non-ascii-module-import/bad.py @@ -0,0 +1,3 @@ +from os.path import join as łos # [non-ascii-module-import] + +foo = łos("a", "b") diff --git a/lib/common/pylint_data/messages/non-ascii-module-import/good.py b/lib/common/pylint_data/messages/non-ascii-module-import/good.py new file mode 100644 index 0000000..388a5c7 --- /dev/null +++ b/lib/common/pylint_data/messages/non-ascii-module-import/good.py @@ -0,0 +1,3 @@ +from os.path import join as os_join + +foo = os_join("a", "b") diff --git a/lib/common/pylint_data/messages/non-ascii-name/bad.py b/lib/common/pylint_data/messages/non-ascii-name/bad.py new file mode 100644 index 0000000..9545327 --- /dev/null +++ b/lib/common/pylint_data/messages/non-ascii-name/bad.py @@ -0,0 +1 @@ +ápple_count = 4444 # [non-ascii-name] diff --git a/lib/common/pylint_data/messages/non-ascii-name/good.py b/lib/common/pylint_data/messages/non-ascii-name/good.py new file mode 100644 index 0000000..bbddb08 --- /dev/null +++ b/lib/common/pylint_data/messages/non-ascii-name/good.py @@ -0,0 +1 @@ +apple_count = 4444 diff --git a/lib/common/pylint_data/messages/non-iterator-returned/bad.py b/lib/common/pylint_data/messages/non-iterator-returned/bad.py new file mode 100644 index 0000000..6e5b99f --- /dev/null +++ b/lib/common/pylint_data/messages/non-iterator-returned/bad.py @@ -0,0 +1,18 @@ +import random + + +class GenericAstrology: + def __init__(self, signs, predictions): + self.signs = signs + self.predictions = predictions + + def __iter__(self): # [non-iterator-returned] + self.index = 0 + self.number_of_prediction = len(self.predictions) + return self + + +SIGNS = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra"] +PREDICTIONS = ["good things", "bad thing", "existential dread"] +for sign, prediction in GenericAstrology(SIGNS, PREDICTIONS): + print(f"{sign} : {prediction} today") diff --git a/lib/common/pylint_data/messages/non-iterator-returned/good.py b/lib/common/pylint_data/messages/non-iterator-returned/good.py new file mode 100644 index 0000000..3517b3f --- /dev/null +++ b/lib/common/pylint_data/messages/non-iterator-returned/good.py @@ -0,0 +1,25 @@ +import random + + +class GenericAstrology: + def __init__(self, signs, predictions): + self.signs = signs + self.predictions = predictions + + def __iter__(self): + self.index = 0 + self.number_of_prediction = len(self.predictions) + return self + + def __next__(self): + if self.index == len(self.signs): + raise StopIteration + self.index += 1 + prediction_index = random.randint(0, self.number_of_prediction - 1) + return self.signs[self.index - 1], self.predictions[prediction_index] + + +SIGNS = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra"] +PREDICTIONS = ["good things", "bad thing", "existential dread"] +for sign, prediction in GenericAstrology(SIGNS, PREDICTIONS): + print(f"{sign} : {prediction} today") diff --git a/lib/common/pylint_data/messages/non-parent-init-called/bad.py b/lib/common/pylint_data/messages/non-parent-init-called/bad.py new file mode 100644 index 0000000..75254db --- /dev/null +++ b/lib/common/pylint_data/messages/non-parent-init-called/bad.py @@ -0,0 +1,15 @@ +class Animal: + def __init__(self): + self.is_multicellular = True + + +class Vertebrate(Animal): + def __init__(self): + super().__init__() + self.has_vertebrae = True + + +class Cat(Vertebrate): + def __init__(self): + Animal.__init__(self) # [non-parent-init-called] + self.is_adorable = True diff --git a/lib/common/pylint_data/messages/non-parent-init-called/good.py b/lib/common/pylint_data/messages/non-parent-init-called/good.py new file mode 100644 index 0000000..bf6abca --- /dev/null +++ b/lib/common/pylint_data/messages/non-parent-init-called/good.py @@ -0,0 +1,15 @@ +class Animal: + def __init__(self): + self.is_multicellular = True + + +class Vertebrate(Animal): + def __init__(self): + super().__init__() + self.has_vertebrae = True + + +class Cat(Vertebrate): + def __init__(self): + super().__init__() + self.is_adorable = True diff --git a/lib/common/pylint_data/messages/non-str-assignment-to-dunder-name/bad.py b/lib/common/pylint_data/messages/non-str-assignment-to-dunder-name/bad.py new file mode 100644 index 0000000..59f13a1 --- /dev/null +++ b/lib/common/pylint_data/messages/non-str-assignment-to-dunder-name/bad.py @@ -0,0 +1,5 @@ +class Fruit: + pass + + +Fruit.__name__ = 1 # [non-str-assignment-to-dunder-name] diff --git a/lib/common/pylint_data/messages/non-str-assignment-to-dunder-name/good.py b/lib/common/pylint_data/messages/non-str-assignment-to-dunder-name/good.py new file mode 100644 index 0000000..ff55f18 --- /dev/null +++ b/lib/common/pylint_data/messages/non-str-assignment-to-dunder-name/good.py @@ -0,0 +1,5 @@ +class Fruit: + pass + + +Fruit.__name__ = "FRUIT" diff --git a/lib/common/pylint_data/messages/nonexistent-operator/bad.py b/lib/common/pylint_data/messages/nonexistent-operator/bad.py new file mode 100644 index 0000000..ab284fa --- /dev/null +++ b/lib/common/pylint_data/messages/nonexistent-operator/bad.py @@ -0,0 +1,5 @@ +i = 0 + +while i <= 10: + print(i) + ++i # [nonexistent-operator] diff --git a/lib/common/pylint_data/messages/nonexistent-operator/good.py b/lib/common/pylint_data/messages/nonexistent-operator/good.py new file mode 100644 index 0000000..e4bbade --- /dev/null +++ b/lib/common/pylint_data/messages/nonexistent-operator/good.py @@ -0,0 +1,5 @@ +i = 0 + +while i <= 10: + print(i) + i += 1 diff --git a/lib/common/pylint_data/messages/nonlocal-and-global/bad.py b/lib/common/pylint_data/messages/nonlocal-and-global/bad.py new file mode 100644 index 0000000..44dd5a3 --- /dev/null +++ b/lib/common/pylint_data/messages/nonlocal-and-global/bad.py @@ -0,0 +1,11 @@ +NUMBER = 42 + + +def update_number(number): # [nonlocal-and-global] + global NUMBER + nonlocal NUMBER + NUMBER = number + print(f"New global number is: {NUMBER}") + + +update_number(24) diff --git a/lib/common/pylint_data/messages/nonlocal-and-global/good.py b/lib/common/pylint_data/messages/nonlocal-and-global/good.py new file mode 100644 index 0000000..f094601 --- /dev/null +++ b/lib/common/pylint_data/messages/nonlocal-and-global/good.py @@ -0,0 +1,10 @@ +NUMBER = 42 + + +def update_number(number): + global NUMBER + NUMBER = number + print(f"New global number is: {NUMBER}") + + +update_number(24) diff --git a/lib/common/pylint_data/messages/nonlocal-without-binding/bad.py b/lib/common/pylint_data/messages/nonlocal-without-binding/bad.py new file mode 100644 index 0000000..6a166e0 --- /dev/null +++ b/lib/common/pylint_data/messages/nonlocal-without-binding/bad.py @@ -0,0 +1,3 @@ +class Fruit: + def get_color(self): + nonlocal colors # [nonlocal-without-binding] diff --git a/lib/common/pylint_data/messages/nonlocal-without-binding/good.py b/lib/common/pylint_data/messages/nonlocal-without-binding/good.py new file mode 100644 index 0000000..cce884a --- /dev/null +++ b/lib/common/pylint_data/messages/nonlocal-without-binding/good.py @@ -0,0 +1,5 @@ +class Fruit: + colors = ["red", "green"] + + def get_color(self): + nonlocal colors diff --git a/lib/common/pylint_data/messages/not-a-mapping/bad.py b/lib/common/pylint_data/messages/not-a-mapping/bad.py new file mode 100644 index 0000000..79ca921 --- /dev/null +++ b/lib/common/pylint_data/messages/not-a-mapping/bad.py @@ -0,0 +1,5 @@ +def print_colors(**colors): + print(colors) + + +print_colors(**list("red", "black")) # [not-a-mapping] diff --git a/lib/common/pylint_data/messages/not-a-mapping/good.py b/lib/common/pylint_data/messages/not-a-mapping/good.py new file mode 100644 index 0000000..3de53ac --- /dev/null +++ b/lib/common/pylint_data/messages/not-a-mapping/good.py @@ -0,0 +1,5 @@ +def print_colors(**colors): + print(colors) + + +print_colors(**dict(red=1, black=2)) diff --git a/lib/common/pylint_data/messages/not-an-iterable/bad.py b/lib/common/pylint_data/messages/not-an-iterable/bad.py new file mode 100644 index 0000000..fa58ff4 --- /dev/null +++ b/lib/common/pylint_data/messages/not-an-iterable/bad.py @@ -0,0 +1,2 @@ +for i in 10: # [not-an-iterable] + pass diff --git a/lib/common/pylint_data/messages/not-an-iterable/good.py b/lib/common/pylint_data/messages/not-an-iterable/good.py new file mode 100644 index 0000000..5781182 --- /dev/null +++ b/lib/common/pylint_data/messages/not-an-iterable/good.py @@ -0,0 +1,2 @@ +for i in "10": + pass diff --git a/lib/common/pylint_data/messages/not-async-context-manager/bad.py b/lib/common/pylint_data/messages/not-async-context-manager/bad.py new file mode 100644 index 0000000..e204023 --- /dev/null +++ b/lib/common/pylint_data/messages/not-async-context-manager/bad.py @@ -0,0 +1,11 @@ +class ContextManager: + def __enter__(self): + pass + + def __exit__(self, *exc): + pass + + +async def foo(): + async with ContextManager(): # [not-async-context-manager] + pass diff --git a/lib/common/pylint_data/messages/not-async-context-manager/details.md b/lib/common/pylint_data/messages/not-async-context-manager/details.md new file mode 100644 index 0000000..9a9845f --- /dev/null +++ b/lib/common/pylint_data/messages/not-async-context-manager/details.md @@ -0,0 +1,2 @@ +Async context manager doesn\'t implement `__aenter__` and `__aexit__`. +It can\'t be emitted when using Python \< 3.5. diff --git a/lib/common/pylint_data/messages/not-async-context-manager/good.py b/lib/common/pylint_data/messages/not-async-context-manager/good.py new file mode 100644 index 0000000..8d527e8 --- /dev/null +++ b/lib/common/pylint_data/messages/not-async-context-manager/good.py @@ -0,0 +1,11 @@ +class AsyncContextManager: + def __aenter__(self): + pass + + def __aexit__(self, *exc): + pass + + +async def foo(): + async with AsyncContextManager(): + pass diff --git a/lib/common/pylint_data/messages/not-callable/bad.py b/lib/common/pylint_data/messages/not-callable/bad.py new file mode 100644 index 0000000..c883515 --- /dev/null +++ b/lib/common/pylint_data/messages/not-callable/bad.py @@ -0,0 +1,2 @@ +NUMBER = 42 +print(NUMBER()) # [not-callable] diff --git a/lib/common/pylint_data/messages/not-callable/good.py b/lib/common/pylint_data/messages/not-callable/good.py new file mode 100644 index 0000000..d8ee66a --- /dev/null +++ b/lib/common/pylint_data/messages/not-callable/good.py @@ -0,0 +1,2 @@ +NUMBER = 42 +print(NUMBER) diff --git a/lib/common/pylint_data/messages/not-context-manager/bad.py b/lib/common/pylint_data/messages/not-context-manager/bad.py new file mode 100644 index 0000000..35107db --- /dev/null +++ b/lib/common/pylint_data/messages/not-context-manager/bad.py @@ -0,0 +1,7 @@ +class MyContextManager: + def __enter__(self): + pass + + +with MyContextManager() as c: # [not-context-manager] + pass diff --git a/lib/common/pylint_data/messages/not-context-manager/good.py b/lib/common/pylint_data/messages/not-context-manager/good.py new file mode 100644 index 0000000..c207457 --- /dev/null +++ b/lib/common/pylint_data/messages/not-context-manager/good.py @@ -0,0 +1,10 @@ +class MyContextManager: + def __enter__(self): + pass + + def __exit__(self, *exc): + pass + + +with MyContextManager() as c: + pass diff --git a/lib/common/pylint_data/messages/not-in-loop/bad.py b/lib/common/pylint_data/messages/not-in-loop/bad.py new file mode 100644 index 0000000..cf54868 --- /dev/null +++ b/lib/common/pylint_data/messages/not-in-loop/bad.py @@ -0,0 +1,6 @@ +def print_even_numbers(): + for i in range(100): + if i % 2 == 0: + print(i) + else: + continue # [not-in-loop] diff --git a/lib/common/pylint_data/messages/not-in-loop/good.py b/lib/common/pylint_data/messages/not-in-loop/good.py new file mode 100644 index 0000000..1b85ff9 --- /dev/null +++ b/lib/common/pylint_data/messages/not-in-loop/good.py @@ -0,0 +1,5 @@ +def print_even_numbers(): + for i in range(100): + if i % 2: + continue + print(i) diff --git a/lib/common/pylint_data/messages/notimplemented-raised/bad.py b/lib/common/pylint_data/messages/notimplemented-raised/bad.py new file mode 100644 index 0000000..4dcf2ef --- /dev/null +++ b/lib/common/pylint_data/messages/notimplemented-raised/bad.py @@ -0,0 +1,3 @@ +class Worm: + def bore(self): + raise NotImplemented # [notimplemented-raised] diff --git a/lib/common/pylint_data/messages/notimplemented-raised/good.py b/lib/common/pylint_data/messages/notimplemented-raised/good.py new file mode 100644 index 0000000..4d38caf --- /dev/null +++ b/lib/common/pylint_data/messages/notimplemented-raised/good.py @@ -0,0 +1,3 @@ +class Worm: + def bore(self): + raise NotImplementedError diff --git a/lib/common/pylint_data/messages/overlapping-except/bad.py b/lib/common/pylint_data/messages/overlapping-except/bad.py new file mode 100644 index 0000000..eaf1e9d --- /dev/null +++ b/lib/common/pylint_data/messages/overlapping-except/bad.py @@ -0,0 +1,5 @@ +def divide_x_by_y(x: float, y: float): + try: + print(x / y) + except (ArithmeticError, FloatingPointError) as e: # [overlapping-except] + print(f"There was an issue: {e}") diff --git a/lib/common/pylint_data/messages/overlapping-except/good.py b/lib/common/pylint_data/messages/overlapping-except/good.py new file mode 100644 index 0000000..0af8ac7 --- /dev/null +++ b/lib/common/pylint_data/messages/overlapping-except/good.py @@ -0,0 +1,18 @@ +def divide_x_by_y(x: float, y: float): + try: + print(x / y) + except FloatingPointError as e: + print(f"There was a FloatingPointError: {e}") + except ArithmeticError as e: + # FloatingPointError were already caught at this point + print(f"There was an OverflowError or a ZeroDivisionError: {e}") + +# or + +def divide_x_by_y(x: float, y: float): + try: + print(x / y) + except ArithmeticError as e: + print( + f"There was an OverflowError, a ZeroDivisionError or a FloatingPointError: {e}" + ) diff --git a/lib/common/pylint_data/messages/overlapping-except/related.md b/lib/common/pylint_data/messages/overlapping-except/related.md new file mode 100644 index 0000000..95810a6 --- /dev/null +++ b/lib/common/pylint_data/messages/overlapping-except/related.md @@ -0,0 +1,2 @@ +- [Exception + hierarchy](https://docs.python.org/3/library/exceptions.html#exception-hierarchy) diff --git a/lib/common/pylint_data/messages/overridden-final-method/bad.py b/lib/common/pylint_data/messages/overridden-final-method/bad.py new file mode 100644 index 0000000..31d7580 --- /dev/null +++ b/lib/common/pylint_data/messages/overridden-final-method/bad.py @@ -0,0 +1,12 @@ +from typing import final + + +class Animal: + @final + def can_breathe(self): + return True + + +class Cat(Animal): + def can_breathe(self): # [overridden-final-method] + pass diff --git a/lib/common/pylint_data/messages/overridden-final-method/details.md b/lib/common/pylint_data/messages/overridden-final-method/details.md new file mode 100644 index 0000000..0711de9 --- /dev/null +++ b/lib/common/pylint_data/messages/overridden-final-method/details.md @@ -0,0 +1 @@ +The message can\'t be emitted when using Python \< 3.8. diff --git a/lib/common/pylint_data/messages/overridden-final-method/good.py b/lib/common/pylint_data/messages/overridden-final-method/good.py new file mode 100644 index 0000000..d95b975 --- /dev/null +++ b/lib/common/pylint_data/messages/overridden-final-method/good.py @@ -0,0 +1,12 @@ +from typing import final + + +class Animal: + @final + def can_breathe(self): + return True + + +class Cat(Animal): + def can_purr(self): + return True diff --git a/lib/common/pylint_data/messages/overridden-final-method/related.md b/lib/common/pylint_data/messages/overridden-final-method/related.md new file mode 100644 index 0000000..c156f30 --- /dev/null +++ b/lib/common/pylint_data/messages/overridden-final-method/related.md @@ -0,0 +1 @@ +- [PEP 591](https://peps.python.org/pep-0591/) diff --git a/lib/common/pylint_data/messages/parse-error/details.md b/lib/common/pylint_data/messages/parse-error/details.md new file mode 100644 index 0000000..0ef4d8c --- /dev/null +++ b/lib/common/pylint_data/messages/parse-error/details.md @@ -0,0 +1,2 @@ +This is a message linked to an internal problem in pylint. There\'s +nothing to change in your code. diff --git a/lib/common/pylint_data/messages/pointless-exception-statement/bad.py b/lib/common/pylint_data/messages/pointless-exception-statement/bad.py new file mode 100644 index 0000000..3dcd1dc --- /dev/null +++ b/lib/common/pylint_data/messages/pointless-exception-statement/bad.py @@ -0,0 +1 @@ +Exception("This exception is a statement.") # [pointless-exception-statement] diff --git a/lib/common/pylint_data/messages/pointless-exception-statement/good.py b/lib/common/pylint_data/messages/pointless-exception-statement/good.py new file mode 100644 index 0000000..718388c --- /dev/null +++ b/lib/common/pylint_data/messages/pointless-exception-statement/good.py @@ -0,0 +1 @@ +raise Exception("This will raise.") diff --git a/lib/common/pylint_data/messages/pointless-statement/bad.py b/lib/common/pylint_data/messages/pointless-statement/bad.py new file mode 100644 index 0000000..defa12c --- /dev/null +++ b/lib/common/pylint_data/messages/pointless-statement/bad.py @@ -0,0 +1 @@ +[1, 2, 3] # [pointless-statement] diff --git a/lib/common/pylint_data/messages/pointless-statement/good.py b/lib/common/pylint_data/messages/pointless-statement/good.py new file mode 100644 index 0000000..fa759b1 --- /dev/null +++ b/lib/common/pylint_data/messages/pointless-statement/good.py @@ -0,0 +1,3 @@ +NUMBERS = [1, 2, 3] + +print(NUMBERS) diff --git a/lib/common/pylint_data/messages/pointless-string-statement/bad.py b/lib/common/pylint_data/messages/pointless-string-statement/bad.py new file mode 100644 index 0000000..8f4410f --- /dev/null +++ b/lib/common/pylint_data/messages/pointless-string-statement/bad.py @@ -0,0 +1,3 @@ +"""This is a docstring which describes the module""" + +"""This is not a docstring""" # [pointless-string-statement] diff --git a/lib/common/pylint_data/messages/pointless-string-statement/good.py b/lib/common/pylint_data/messages/pointless-string-statement/good.py new file mode 100644 index 0000000..33bc822 --- /dev/null +++ b/lib/common/pylint_data/messages/pointless-string-statement/good.py @@ -0,0 +1,3 @@ +"""This is a docstring which describes the module""" + +# This is comment which describes a particular part of the module. diff --git a/lib/common/pylint_data/messages/pointless-string-statement/related.md b/lib/common/pylint_data/messages/pointless-string-statement/related.md new file mode 100644 index 0000000..75e1530 --- /dev/null +++ b/lib/common/pylint_data/messages/pointless-string-statement/related.md @@ -0,0 +1,2 @@ +- [Discussion thread re: docstrings on + assignments](https://discuss.python.org/t/docstrings-for-new-type-aliases-as-defined-in-pep-695/39816) diff --git a/lib/common/pylint_data/messages/positional-only-arguments-expected/bad.py b/lib/common/pylint_data/messages/positional-only-arguments-expected/bad.py new file mode 100644 index 0000000..75e63e5 --- /dev/null +++ b/lib/common/pylint_data/messages/positional-only-arguments-expected/bad.py @@ -0,0 +1,6 @@ +def cube(n, /): + """Takes in a number n, returns the cube of n""" + return n**3 + + +cube(n=2) # [positional-only-arguments-expected] diff --git a/lib/common/pylint_data/messages/positional-only-arguments-expected/good.py b/lib/common/pylint_data/messages/positional-only-arguments-expected/good.py new file mode 100644 index 0000000..77cdc80 --- /dev/null +++ b/lib/common/pylint_data/messages/positional-only-arguments-expected/good.py @@ -0,0 +1,6 @@ +def cube(n, /): + """Takes in a number n, returns the cube of n""" + return n**3 + + +cube(2) diff --git a/lib/common/pylint_data/messages/positional-only-arguments-expected/related.md b/lib/common/pylint_data/messages/positional-only-arguments-expected/related.md new file mode 100644 index 0000000..b13165e --- /dev/null +++ b/lib/common/pylint_data/messages/positional-only-arguments-expected/related.md @@ -0,0 +1 @@ +- [PEP 570](https://peps.python.org/pep-570/) diff --git a/lib/common/pylint_data/messages/possibly-unused-variable/bad.py b/lib/common/pylint_data/messages/possibly-unused-variable/bad.py new file mode 100644 index 0000000..b64aee8 --- /dev/null +++ b/lib/common/pylint_data/messages/possibly-unused-variable/bad.py @@ -0,0 +1,4 @@ +def choose_fruits(fruits): + print(fruits) + color = "red" # [possibly-unused-variable] + return locals() diff --git a/lib/common/pylint_data/messages/possibly-unused-variable/good.py b/lib/common/pylint_data/messages/possibly-unused-variable/good.py new file mode 100644 index 0000000..0951183 --- /dev/null +++ b/lib/common/pylint_data/messages/possibly-unused-variable/good.py @@ -0,0 +1,6 @@ +def choose_fruits(fruits): + current_locals = locals() + print(fruits) + color = "red" + print(color) + return current_locals diff --git a/lib/common/pylint_data/messages/possibly-used-before-assignment/bad.py b/lib/common/pylint_data/messages/possibly-used-before-assignment/bad.py new file mode 100644 index 0000000..8e7f0cb --- /dev/null +++ b/lib/common/pylint_data/messages/possibly-used-before-assignment/bad.py @@ -0,0 +1,4 @@ +def check_lunchbox(items: list[str]): + if not items: + empty = True + print(empty) # [possibly-used-before-assignment] diff --git a/lib/common/pylint_data/messages/possibly-used-before-assignment/details.md b/lib/common/pylint_data/messages/possibly-used-before-assignment/details.md new file mode 100644 index 0000000..72f6b52 --- /dev/null +++ b/lib/common/pylint_data/messages/possibly-used-before-assignment/details.md @@ -0,0 +1,55 @@ +You can use `assert_never` to mark exhaustive choices: + +``` python +from typing import assert_never + +def handle_date_suffix(suffix): + if suffix == "d": + ... + elif suffix == "m": + ... + elif suffix == "y": + ... + else: + assert_never(suffix) + +if suffix in "dmy": + handle_date_suffix(suffix) +``` + +Or, instead of [assert_never()]{.title-ref}, you can call a function +with a return annotation of [Never]{.title-ref} or +[NoReturn]{.title-ref}. Unlike in the general case, where by design +pylint ignores type annotations and does its own static analysis, here, +pylint treats these special annotations like a disable comment. + +Pylint currently allows repeating the same test like this, even though +this lets some error cases through, as pylint does not assess the +intervening code: + +``` python +if guarded(): + var = 1 + +# what if code here affects the result of guarded()? + +if guarded(): + print(var) +``` + +But this exception is limited to the repeating the exact same test. This +warns: + +``` python +if guarded(): + var = 1 + +if guarded() or other_condition: + print(var) # [possibly-used-before-assignment] +``` + +If you find this surprising, consider that pylint, as a static analysis +tool, does not know if `guarded()` is deterministic or talks to a +database. For variables (e.g. `guarded` versus `guarded()`), this is +less of an issue, so in this case, `possibly-used-before-assignment` +acts more like a future-proofing style preference than an error, per se. diff --git a/lib/common/pylint_data/messages/possibly-used-before-assignment/good.py b/lib/common/pylint_data/messages/possibly-used-before-assignment/good.py new file mode 100644 index 0000000..6bb478f --- /dev/null +++ b/lib/common/pylint_data/messages/possibly-used-before-assignment/good.py @@ -0,0 +1,5 @@ +def check_lunchbox(items: list[str]): + empty = False + if not items: + empty = True + print(empty) diff --git a/lib/common/pylint_data/messages/potential-index-error/bad.py b/lib/common/pylint_data/messages/potential-index-error/bad.py new file mode 100644 index 0000000..67d9bbe --- /dev/null +++ b/lib/common/pylint_data/messages/potential-index-error/bad.py @@ -0,0 +1 @@ +print([1, 2, 3][3]) # [potential-index-error] diff --git a/lib/common/pylint_data/messages/potential-index-error/good.py b/lib/common/pylint_data/messages/potential-index-error/good.py new file mode 100644 index 0000000..bceb129 --- /dev/null +++ b/lib/common/pylint_data/messages/potential-index-error/good.py @@ -0,0 +1 @@ +print([1, 2, 3][2]) diff --git a/lib/common/pylint_data/messages/prefer-typing-namedtuple/bad.py b/lib/common/pylint_data/messages/prefer-typing-namedtuple/bad.py new file mode 100644 index 0000000..d555b0f --- /dev/null +++ b/lib/common/pylint_data/messages/prefer-typing-namedtuple/bad.py @@ -0,0 +1,5 @@ +from collections import namedtuple + +Philosophy = namedtuple( # [prefer-typing-namedtuple] + "Philosophy", ("goodness", "truth", "beauty") +) diff --git a/lib/common/pylint_data/messages/prefer-typing-namedtuple/good.py b/lib/common/pylint_data/messages/prefer-typing-namedtuple/good.py new file mode 100644 index 0000000..ef094aa --- /dev/null +++ b/lib/common/pylint_data/messages/prefer-typing-namedtuple/good.py @@ -0,0 +1,7 @@ +from typing import NamedTuple + + +class Philosophy(NamedTuple): + goodness: str + truth: bool + beauty: float diff --git a/lib/common/pylint_data/messages/prefer-typing-namedtuple/related.md b/lib/common/pylint_data/messages/prefer-typing-namedtuple/related.md new file mode 100644 index 0000000..dade346 --- /dev/null +++ b/lib/common/pylint_data/messages/prefer-typing-namedtuple/related.md @@ -0,0 +1 @@ +- [typing.NamedTuple](https://docs.python.org/3/library/typing.html#typing.NamedTuple) diff --git a/lib/common/pylint_data/messages/preferred-module/bad.py b/lib/common/pylint_data/messages/preferred-module/bad.py new file mode 100644 index 0000000..a047ff3 --- /dev/null +++ b/lib/common/pylint_data/messages/preferred-module/bad.py @@ -0,0 +1 @@ +import urllib # [preferred-module] diff --git a/lib/common/pylint_data/messages/preferred-module/good.py b/lib/common/pylint_data/messages/preferred-module/good.py new file mode 100644 index 0000000..20b1553 --- /dev/null +++ b/lib/common/pylint_data/messages/preferred-module/good.py @@ -0,0 +1 @@ +import requests diff --git a/lib/common/pylint_data/messages/property-with-parameters/bad.py b/lib/common/pylint_data/messages/property-with-parameters/bad.py new file mode 100644 index 0000000..d80ca14 --- /dev/null +++ b/lib/common/pylint_data/messages/property-with-parameters/bad.py @@ -0,0 +1,4 @@ +class Worm: + @property + def bore(self, depth): # [property-with-parameters] + pass diff --git a/lib/common/pylint_data/messages/property-with-parameters/good.py b/lib/common/pylint_data/messages/property-with-parameters/good.py new file mode 100644 index 0000000..af6a4fb --- /dev/null +++ b/lib/common/pylint_data/messages/property-with-parameters/good.py @@ -0,0 +1,9 @@ +class Worm: + @property + def bore(self): + """Property accessed with '.bore'.""" + pass + + def bore_with_depth(depth): + """Function called with .bore_with_depth(depth).""" + pass diff --git a/lib/common/pylint_data/messages/protected-access/bad.py b/lib/common/pylint_data/messages/protected-access/bad.py new file mode 100644 index 0000000..150138f --- /dev/null +++ b/lib/common/pylint_data/messages/protected-access/bad.py @@ -0,0 +1,7 @@ +class Worm: + def __swallow(self): + pass + + +jim = Worm() +jim.__swallow() # [protected-access] diff --git a/lib/common/pylint_data/messages/protected-access/good.py b/lib/common/pylint_data/messages/protected-access/good.py new file mode 100644 index 0000000..dddfdd1 --- /dev/null +++ b/lib/common/pylint_data/messages/protected-access/good.py @@ -0,0 +1,10 @@ +class Worm: + def __swallow(self): + pass + + def eat(self): + return self.__swallow() + + +jim = Worm() +jim.eat() diff --git a/lib/common/pylint_data/messages/raise-missing-from/bad.py b/lib/common/pylint_data/messages/raise-missing-from/bad.py new file mode 100644 index 0000000..8b5ad26 --- /dev/null +++ b/lib/common/pylint_data/messages/raise-missing-from/bad.py @@ -0,0 +1,4 @@ +try: + 1 / 0 +except ZeroDivisionError as e: + raise ValueError("Rectangle Area cannot be zero") # [raise-missing-from] diff --git a/lib/common/pylint_data/messages/raise-missing-from/good.py b/lib/common/pylint_data/messages/raise-missing-from/good.py new file mode 100644 index 0000000..9a5cdef --- /dev/null +++ b/lib/common/pylint_data/messages/raise-missing-from/good.py @@ -0,0 +1,4 @@ +try: + 1 / 0 +except ZeroDivisionError as e: + raise ValueError("Rectangle Area cannot be zero") from e diff --git a/lib/common/pylint_data/messages/raise-missing-from/related.md b/lib/common/pylint_data/messages/raise-missing-from/related.md new file mode 100644 index 0000000..3ff062a --- /dev/null +++ b/lib/common/pylint_data/messages/raise-missing-from/related.md @@ -0,0 +1 @@ +- [PEP 3134](https://peps.python.org/pep-3134/) diff --git a/lib/common/pylint_data/messages/raising-bad-type/bad.py b/lib/common/pylint_data/messages/raising-bad-type/bad.py new file mode 100644 index 0000000..2614764 --- /dev/null +++ b/lib/common/pylint_data/messages/raising-bad-type/bad.py @@ -0,0 +1,10 @@ +class FasterThanTheSpeedOfLightError(ZeroDivisionError): + def __init__(self): + super().__init__("You can't go faster than the speed of light !") + + +def calculate_speed(distance: float, time: float) -> float: + try: + return distance / time + except ZeroDivisionError as e: + raise None # [raising-bad-type] diff --git a/lib/common/pylint_data/messages/raising-bad-type/good.py b/lib/common/pylint_data/messages/raising-bad-type/good.py new file mode 100644 index 0000000..a269492 --- /dev/null +++ b/lib/common/pylint_data/messages/raising-bad-type/good.py @@ -0,0 +1,10 @@ +class FasterThanTheSpeedOfLightError(ZeroDivisionError): + def __init__(self): + super().__init__("You can't go faster than the speed of light !") + + +def calculate_speed(distance: float, time: float) -> float: + try: + return distance / time + except ZeroDivisionError as e: + raise FasterThanTheSpeedOfLightError() from e diff --git a/lib/common/pylint_data/messages/raising-format-tuple/bad.py b/lib/common/pylint_data/messages/raising-format-tuple/bad.py new file mode 100644 index 0000000..50519bc --- /dev/null +++ b/lib/common/pylint_data/messages/raising-format-tuple/bad.py @@ -0,0 +1 @@ +raise RuntimeError("This looks wrong %s %s", ("a", "b")) # [raising-format-tuple] diff --git a/lib/common/pylint_data/messages/raising-format-tuple/good.py b/lib/common/pylint_data/messages/raising-format-tuple/good.py new file mode 100644 index 0000000..d368710 --- /dev/null +++ b/lib/common/pylint_data/messages/raising-format-tuple/good.py @@ -0,0 +1 @@ +raise RuntimeError("This looks wrong %s %s" % ("a", "b")) diff --git a/lib/common/pylint_data/messages/raising-non-exception/bad.py b/lib/common/pylint_data/messages/raising-non-exception/bad.py new file mode 100644 index 0000000..fba3b87 --- /dev/null +++ b/lib/common/pylint_data/messages/raising-non-exception/bad.py @@ -0,0 +1 @@ +raise str # [raising-non-exception] diff --git a/lib/common/pylint_data/messages/raising-non-exception/good.py b/lib/common/pylint_data/messages/raising-non-exception/good.py new file mode 100644 index 0000000..fe382e9 --- /dev/null +++ b/lib/common/pylint_data/messages/raising-non-exception/good.py @@ -0,0 +1 @@ +raise Exception("Goodbye world !") diff --git a/lib/common/pylint_data/messages/raw-checker-failed/details.md b/lib/common/pylint_data/messages/raw-checker-failed/details.md new file mode 100644 index 0000000..25377ba --- /dev/null +++ b/lib/common/pylint_data/messages/raw-checker-failed/details.md @@ -0,0 +1,3 @@ +This warns you that a builtin module was impossible to analyse (an ast +node is not pure python). There\'s nothing to change in your code, this +is a warning about astroid and pylint\'s limitations. diff --git a/lib/common/pylint_data/messages/redeclared-assigned-name/bad.py b/lib/common/pylint_data/messages/redeclared-assigned-name/bad.py new file mode 100644 index 0000000..39e6182 --- /dev/null +++ b/lib/common/pylint_data/messages/redeclared-assigned-name/bad.py @@ -0,0 +1 @@ +FIRST, FIRST = (1, 2) # [redeclared-assigned-name] diff --git a/lib/common/pylint_data/messages/redeclared-assigned-name/good.py b/lib/common/pylint_data/messages/redeclared-assigned-name/good.py new file mode 100644 index 0000000..e51bacd --- /dev/null +++ b/lib/common/pylint_data/messages/redeclared-assigned-name/good.py @@ -0,0 +1 @@ +FIRST, SECOND = (1, 2) diff --git a/lib/common/pylint_data/messages/redefined-argument-from-local/bad.py b/lib/common/pylint_data/messages/redefined-argument-from-local/bad.py new file mode 100644 index 0000000..733f307 --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-argument-from-local/bad.py @@ -0,0 +1,4 @@ +def show(host_id=10.11): + # +1: [redefined-argument-from-local] + for host_id, host in [[12.13, "Venus"], [14.15, "Mars"]]: + print(host_id, host) diff --git a/lib/common/pylint_data/messages/redefined-argument-from-local/good.py b/lib/common/pylint_data/messages/redefined-argument-from-local/good.py new file mode 100644 index 0000000..fd7c081 --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-argument-from-local/good.py @@ -0,0 +1,3 @@ +def show(host_id=10.11): + for inner_host_id, host in [[12.13, "Venus"], [14.15, "Mars"]]: + print(host_id, inner_host_id, host) diff --git a/lib/common/pylint_data/messages/redefined-builtin/bad.py b/lib/common/pylint_data/messages/redefined-builtin/bad.py new file mode 100644 index 0000000..da9bda8 --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-builtin/bad.py @@ -0,0 +1,2 @@ +def map(): # [redefined-builtin] + pass diff --git a/lib/common/pylint_data/messages/redefined-builtin/details.md b/lib/common/pylint_data/messages/redefined-builtin/details.md new file mode 100644 index 0000000..53203a7 --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-builtin/details.md @@ -0,0 +1,6 @@ +Shadowing [built-ins](https://docs.python.org/3.13/library/functions.html) at the global scope is discouraged because it +obscures their behavior throughout the entire module, increasing the +risk of subtle bugs when the built-in is needed elsewhere. + + In contrast, local redefinitions _might_ be acceptable as their impact is confined to a +specific scope; although it is generally not a good idea. diff --git a/lib/common/pylint_data/messages/redefined-builtin/good.py b/lib/common/pylint_data/messages/redefined-builtin/good.py new file mode 100644 index 0000000..ab733ed --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-builtin/good.py @@ -0,0 +1,2 @@ +def map_iterable(): + pass diff --git a/lib/common/pylint_data/messages/redefined-loop-name/bad.py b/lib/common/pylint_data/messages/redefined-loop-name/bad.py new file mode 100644 index 0000000..5dd6543 --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-loop-name/bad.py @@ -0,0 +1,3 @@ +def normalize_names(names): + for name in names: + name = name.lower() # [redefined-loop-name] diff --git a/lib/common/pylint_data/messages/redefined-loop-name/good.py b/lib/common/pylint_data/messages/redefined-loop-name/good.py new file mode 100644 index 0000000..0b245b0 --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-loop-name/good.py @@ -0,0 +1,3 @@ +def normalize_names(names): + for name in names: + lowercased_name = name.lower() diff --git a/lib/common/pylint_data/messages/redefined-outer-name/bad.py b/lib/common/pylint_data/messages/redefined-outer-name/bad.py new file mode 100644 index 0000000..3d03c9c --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-outer-name/bad.py @@ -0,0 +1,6 @@ +count = 10 + + +def count_it(count): # [redefined-outer-name] + for i in range(count): + print(i) diff --git a/lib/common/pylint_data/messages/redefined-outer-name/details.md b/lib/common/pylint_data/messages/redefined-outer-name/details.md new file mode 100644 index 0000000..459862e --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-outer-name/details.md @@ -0,0 +1,26 @@ +A common issue is that this message is triggered when using +[pytest]{.title-ref} +[fixtures](https://docs.pytest.org/en/7.1.x/how-to/fixtures.html): + +``` python +import pytest + +@pytest.fixture +def setup(): + ... + + +def test_something(setup): # [redefined-outer-name] + ... +``` + +One solution to this problem is to explicitly name the fixture: + +``` python +@pytest.fixture(name="setup") +def setup_fixture(): + ... +``` + +Alternatively [pylint]{.title-ref} plugins like +[pylint-pytest](https://pypi.org/project/pylint-pytest/) can be used. diff --git a/lib/common/pylint_data/messages/redefined-outer-name/good.py b/lib/common/pylint_data/messages/redefined-outer-name/good.py new file mode 100644 index 0000000..1350598 --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-outer-name/good.py @@ -0,0 +1,6 @@ +count = 10 + + +def count_it(limit): + for i in range(limit): + print(i) diff --git a/lib/common/pylint_data/messages/redefined-slots-in-subclass/bad.py b/lib/common/pylint_data/messages/redefined-slots-in-subclass/bad.py new file mode 100644 index 0000000..e47b2b8 --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-slots-in-subclass/bad.py @@ -0,0 +1,6 @@ +class Base: + __slots__ = ("a", "b") + + +class Subclass(Base): + __slots__ = ("a", "d") # [redefined-slots-in-subclass] diff --git a/lib/common/pylint_data/messages/redefined-slots-in-subclass/good.py b/lib/common/pylint_data/messages/redefined-slots-in-subclass/good.py new file mode 100644 index 0000000..26c606a --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-slots-in-subclass/good.py @@ -0,0 +1,6 @@ +class Base: + __slots__ = ("a", "b") + + +class Subclass(Base): + __slots__ = ("d",) diff --git a/lib/common/pylint_data/messages/redefined-variable-type/bad.py b/lib/common/pylint_data/messages/redefined-variable-type/bad.py new file mode 100644 index 0000000..02b9394 --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-variable-type/bad.py @@ -0,0 +1,2 @@ +x = 1 +x = "2" # [redefined-variable-type] diff --git a/lib/common/pylint_data/messages/redefined-variable-type/good.py b/lib/common/pylint_data/messages/redefined-variable-type/good.py new file mode 100644 index 0000000..c3afc3f --- /dev/null +++ b/lib/common/pylint_data/messages/redefined-variable-type/good.py @@ -0,0 +1,2 @@ +x = 1 +x = 2 diff --git a/lib/common/pylint_data/messages/redundant-keyword-arg/bad.py b/lib/common/pylint_data/messages/redundant-keyword-arg/bad.py new file mode 100644 index 0000000..8e2f9b6 --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-keyword-arg/bad.py @@ -0,0 +1,5 @@ +def square(x): + return x * x + + +square(5, x=4) # [redundant-keyword-arg] diff --git a/lib/common/pylint_data/messages/redundant-keyword-arg/good.py b/lib/common/pylint_data/messages/redundant-keyword-arg/good.py new file mode 100644 index 0000000..b39b5db --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-keyword-arg/good.py @@ -0,0 +1,11 @@ +def square(x): + return x * x + +# Using an arg +square(5) + +def square(x): + return x * x + +# Using a kwarg (keyword arg) +square(x=4) diff --git a/lib/common/pylint_data/messages/redundant-returns-doc/bad.py b/lib/common/pylint_data/messages/redundant-returns-doc/bad.py new file mode 100644 index 0000000..5d018db --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-returns-doc/bad.py @@ -0,0 +1,9 @@ +def print_fruits(fruits): # [redundant-returns-doc] + """Print list of fruits + + Returns + ------- + str + """ + print(fruits) + return None diff --git a/lib/common/pylint_data/messages/redundant-returns-doc/good.py b/lib/common/pylint_data/messages/redundant-returns-doc/good.py new file mode 100644 index 0000000..7f3eeeb --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-returns-doc/good.py @@ -0,0 +1,9 @@ +def print_fruits(fruits): + """Print list of fruits + + Returns + ------- + str + """ + print(fruits) + return ",".join(fruits) diff --git a/lib/common/pylint_data/messages/redundant-typehint-argument/bad.py b/lib/common/pylint_data/messages/redundant-typehint-argument/bad.py new file mode 100644 index 0000000..1717593 --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-typehint-argument/bad.py @@ -0,0 +1,3 @@ +from typing import Union + +sweet_count: Union[int, str, int] = 42 # [redundant-typehint-argument] diff --git a/lib/common/pylint_data/messages/redundant-typehint-argument/good.py b/lib/common/pylint_data/messages/redundant-typehint-argument/good.py new file mode 100644 index 0000000..ce5e3c0 --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-typehint-argument/good.py @@ -0,0 +1,3 @@ +from typing import Union + +sweet_count: Union[str, int] = 42 diff --git a/lib/common/pylint_data/messages/redundant-u-string-prefix/bad.py b/lib/common/pylint_data/messages/redundant-u-string-prefix/bad.py new file mode 100644 index 0000000..bae9738 --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-u-string-prefix/bad.py @@ -0,0 +1,2 @@ +def print_fruit(): + print(u"Apple") # [redundant-u-string-prefix] diff --git a/lib/common/pylint_data/messages/redundant-u-string-prefix/good.py b/lib/common/pylint_data/messages/redundant-u-string-prefix/good.py new file mode 100644 index 0000000..64b2d50 --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-u-string-prefix/good.py @@ -0,0 +1,2 @@ +def print_fruit(): + print("Apple") diff --git a/lib/common/pylint_data/messages/redundant-unittest-assert/bad.py b/lib/common/pylint_data/messages/redundant-unittest-assert/bad.py new file mode 100644 index 0000000..dcfc2e2 --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-unittest-assert/bad.py @@ -0,0 +1,6 @@ +import unittest + + +class DummyTestCase(unittest.TestCase): + def test_dummy(self): + self.assertTrue("foo") # [redundant-unittest-assert] diff --git a/lib/common/pylint_data/messages/redundant-unittest-assert/details.md b/lib/common/pylint_data/messages/redundant-unittest-assert/details.md new file mode 100644 index 0000000..a14ffc9 --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-unittest-assert/details.md @@ -0,0 +1,6 @@ +Directly asserting a string literal will always pass. The solution is to +test something that could fail, or not assert at all. + +For assertions using `assert` there are similar messages: +`assert-on-string-literal`{.interpreted-text role="ref"} and +`assert-on-tuple`{.interpreted-text role="ref"}. diff --git a/lib/common/pylint_data/messages/redundant-unittest-assert/good.py b/lib/common/pylint_data/messages/redundant-unittest-assert/good.py new file mode 100644 index 0000000..e0e862d --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-unittest-assert/good.py @@ -0,0 +1,7 @@ +import unittest + + +class DummyTestCase(unittest.TestCase): + def test_dummy(self): + actual = "test_result" + self.assertEqual(actual, "expected") diff --git a/lib/common/pylint_data/messages/redundant-unittest-assert/related.md b/lib/common/pylint_data/messages/redundant-unittest-assert/related.md new file mode 100644 index 0000000..a8952ab --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-unittest-assert/related.md @@ -0,0 +1,5 @@ +- [Tests without assertion](https://stackoverflow.com/a/137418/2519059) +- [Testing that there is no error + raised](https://stackoverflow.com/questions/20274987) +- [Parametrizing conditional + raising](https://docs.pytest.org/en/latest/example/parametrize.html#parametrizing-conditional-raising) diff --git a/lib/common/pylint_data/messages/redundant-yields-doc/bad.py b/lib/common/pylint_data/messages/redundant-yields-doc/bad.py new file mode 100644 index 0000000..c2d1e68 --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-yields-doc/bad.py @@ -0,0 +1,9 @@ +def give_fruits(fruits): # [redundant-yields-doc] + """Something about fruits + + Yields + ------- + list + fruits + """ + return fruits diff --git a/lib/common/pylint_data/messages/redundant-yields-doc/good.py b/lib/common/pylint_data/messages/redundant-yields-doc/good.py new file mode 100644 index 0000000..1055a0c --- /dev/null +++ b/lib/common/pylint_data/messages/redundant-yields-doc/good.py @@ -0,0 +1,10 @@ +def give_fruits(fruits): + """Something about fruits + + Yields + ------- + str + fruit + """ + for fruit in fruits: + yield fruit diff --git a/lib/common/pylint_data/messages/reimported/bad.py b/lib/common/pylint_data/messages/reimported/bad.py new file mode 100644 index 0000000..c9ab065 --- /dev/null +++ b/lib/common/pylint_data/messages/reimported/bad.py @@ -0,0 +1,2 @@ +import re +import re # [reimported] diff --git a/lib/common/pylint_data/messages/reimported/good.py b/lib/common/pylint_data/messages/reimported/good.py new file mode 100644 index 0000000..b199df5 --- /dev/null +++ b/lib/common/pylint_data/messages/reimported/good.py @@ -0,0 +1 @@ +import re diff --git a/lib/common/pylint_data/messages/relative-beyond-top-level/bad.py b/lib/common/pylint_data/messages/relative-beyond-top-level/bad.py new file mode 100644 index 0000000..89d0aee --- /dev/null +++ b/lib/common/pylint_data/messages/relative-beyond-top-level/bad.py @@ -0,0 +1 @@ +from ................antigravity import NGField # [relative-beyond-top-level] diff --git a/lib/common/pylint_data/messages/relative-beyond-top-level/details.md b/lib/common/pylint_data/messages/relative-beyond-top-level/details.md new file mode 100644 index 0000000..0c87c6e --- /dev/null +++ b/lib/common/pylint_data/messages/relative-beyond-top-level/details.md @@ -0,0 +1,5 @@ +Absolute imports were strongly preferred, historically. Relative imports +allow you to reorganize packages without changing any code, but these +days refactoring tools and IDEs allow you to do that at almost no cost +anyway if the imports are explicit/absolute. Therefore, absolute imports +are often still preferred over relative ones. diff --git a/lib/common/pylint_data/messages/relative-beyond-top-level/good.py b/lib/common/pylint_data/messages/relative-beyond-top-level/good.py new file mode 100644 index 0000000..6048cc2 --- /dev/null +++ b/lib/common/pylint_data/messages/relative-beyond-top-level/good.py @@ -0,0 +1,7 @@ +# Absolute import +from physic.antigravity import NGField + +# or + +# Right number of dots in the import: you needed 15 dots, not 16, duh. +# from ...............antigravity import NGField diff --git a/lib/common/pylint_data/messages/relative-beyond-top-level/related.md b/lib/common/pylint_data/messages/relative-beyond-top-level/related.md new file mode 100644 index 0000000..327c37a --- /dev/null +++ b/lib/common/pylint_data/messages/relative-beyond-top-level/related.md @@ -0,0 +1,4 @@ +- [Absolute vs. explicit relative import of Python + module](https://stackoverflow.com/a/16748366/2519059) +- [Withdraw anti-recommendation of relative imports from + documentation](https://bugs.python.org/msg118031) diff --git a/lib/common/pylint_data/messages/repeated-keyword/bad.py b/lib/common/pylint_data/messages/repeated-keyword/bad.py new file mode 100644 index 0000000..5fd2d94 --- /dev/null +++ b/lib/common/pylint_data/messages/repeated-keyword/bad.py @@ -0,0 +1,6 @@ +def func(a, b, c): + return a, b, c + + +func(1, 2, c=3, **{"c": 4}) # [repeated-keyword] +func(1, 2, **{"c": 3}, **{"c": 4}) # [repeated-keyword] diff --git a/lib/common/pylint_data/messages/repeated-keyword/good.py b/lib/common/pylint_data/messages/repeated-keyword/good.py new file mode 100644 index 0000000..f7bba62 --- /dev/null +++ b/lib/common/pylint_data/messages/repeated-keyword/good.py @@ -0,0 +1,5 @@ +def func(a, b, c): + return a, b, c + + +func(1, 2, c=3) diff --git a/lib/common/pylint_data/messages/return-arg-in-generator/details.md b/lib/common/pylint_data/messages/return-arg-in-generator/details.md new file mode 100644 index 0000000..78a4de2 --- /dev/null +++ b/lib/common/pylint_data/messages/return-arg-in-generator/details.md @@ -0,0 +1,16 @@ +This is a message that isn\'t going to be raised for python \> 3.3. It +was raised for code like: + + def interrogate_until_you_find_jack(pirates): + for pirate in pirates: + if pirate == "Captain Jack Sparrow": + return "Arrr! We've found our captain!" + yield pirate + +Which is now valid and equivalent to the previously expected: + + def interrogate_until_you_find_jack(pirates): + for pirate in pirates: + if pirate == "Captain Jack Sparrow": + raise StopIteration("Arrr! We've found our captain!") + yield pirate diff --git a/lib/common/pylint_data/messages/return-arg-in-generator/good.py b/lib/common/pylint_data/messages/return-arg-in-generator/good.py new file mode 100644 index 0000000..72dc95e --- /dev/null +++ b/lib/common/pylint_data/messages/return-arg-in-generator/good.py @@ -0,0 +1,4 @@ +def yield_numbers(): + for number in range(10): + yield number + return "I am now allowed!" # This was not allowed in Python 3.3 and earlier. diff --git a/lib/common/pylint_data/messages/return-arg-in-generator/related.md b/lib/common/pylint_data/messages/return-arg-in-generator/related.md new file mode 100644 index 0000000..c122f28 --- /dev/null +++ b/lib/common/pylint_data/messages/return-arg-in-generator/related.md @@ -0,0 +1,3 @@ +- [PEP380](https://peps.python.org/pep-0380/) +- [Stackoverflow + explanation](https://stackoverflow.com/a/16780113/2519059) diff --git a/lib/common/pylint_data/messages/return-in-finally/bad.py b/lib/common/pylint_data/messages/return-in-finally/bad.py new file mode 100644 index 0000000..55ac801 --- /dev/null +++ b/lib/common/pylint_data/messages/return-in-finally/bad.py @@ -0,0 +1,7 @@ +def second_favorite(): + fruits = ["kiwi", "pineapple"] + try: + return fruits[1] + finally: + # because of this `return` statement, this function will always return "kiwi" + return fruits[0] # [return-in-finally] diff --git a/lib/common/pylint_data/messages/return-in-finally/good.py b/lib/common/pylint_data/messages/return-in-finally/good.py new file mode 100644 index 0000000..e1d515c --- /dev/null +++ b/lib/common/pylint_data/messages/return-in-finally/good.py @@ -0,0 +1,8 @@ +def second_favorite(): + fruits = ["kiwi", "pineapple"] + try: + return fruits[1] + except KeyError: + ... + + return fruits[0] diff --git a/lib/common/pylint_data/messages/return-in-finally/related.md b/lib/common/pylint_data/messages/return-in-finally/related.md new file mode 100644 index 0000000..8def6ef --- /dev/null +++ b/lib/common/pylint_data/messages/return-in-finally/related.md @@ -0,0 +1,4 @@ +- [Python 3 docs \'finally\' + clause](https://docs.python.org/3/reference/compound_stmts.html#finally-clause) +- [PEP 765 - Disallow return/break/continue that exit a finally + block](https://peps.python.org/pep-0765/) diff --git a/lib/common/pylint_data/messages/return-in-init/bad.py b/lib/common/pylint_data/messages/return-in-init/bad.py new file mode 100644 index 0000000..044174e --- /dev/null +++ b/lib/common/pylint_data/messages/return-in-init/bad.py @@ -0,0 +1,3 @@ +class Sum: + def __init__(self, a, b): # [return-in-init] + return a + b diff --git a/lib/common/pylint_data/messages/return-in-init/good.py b/lib/common/pylint_data/messages/return-in-init/good.py new file mode 100644 index 0000000..0efe05a --- /dev/null +++ b/lib/common/pylint_data/messages/return-in-init/good.py @@ -0,0 +1,3 @@ +class Sum: + def __init__(self, a, b) -> None: + self.result = a + b diff --git a/lib/common/pylint_data/messages/return-in-init/related.md b/lib/common/pylint_data/messages/return-in-init/related.md new file mode 100644 index 0000000..96a2c59 --- /dev/null +++ b/lib/common/pylint_data/messages/return-in-init/related.md @@ -0,0 +1,2 @@ +- [\_\_init\_\_ method + documentation](https://docs.python.org/3/reference/datamodel.html#object.__init__) diff --git a/lib/common/pylint_data/messages/return-outside-function/bad.py b/lib/common/pylint_data/messages/return-outside-function/bad.py new file mode 100644 index 0000000..c5b579b --- /dev/null +++ b/lib/common/pylint_data/messages/return-outside-function/bad.py @@ -0,0 +1 @@ +return 42 # [return-outside-function] diff --git a/lib/common/pylint_data/messages/return-outside-function/good.py b/lib/common/pylint_data/messages/return-outside-function/good.py new file mode 100644 index 0000000..de451ec --- /dev/null +++ b/lib/common/pylint_data/messages/return-outside-function/good.py @@ -0,0 +1,2 @@ +def get_the_answer(): + return 42 diff --git a/lib/common/pylint_data/messages/self-assigning-variable/bad.py b/lib/common/pylint_data/messages/self-assigning-variable/bad.py new file mode 100644 index 0000000..91c0c58 --- /dev/null +++ b/lib/common/pylint_data/messages/self-assigning-variable/bad.py @@ -0,0 +1,2 @@ +year = 2000 +year = year # [self-assigning-variable] diff --git a/lib/common/pylint_data/messages/self-assigning-variable/good.py b/lib/common/pylint_data/messages/self-assigning-variable/good.py new file mode 100644 index 0000000..dc75526 --- /dev/null +++ b/lib/common/pylint_data/messages/self-assigning-variable/good.py @@ -0,0 +1 @@ +year = 2000 diff --git a/lib/common/pylint_data/messages/self-assigning-variable/related.md b/lib/common/pylint_data/messages/self-assigning-variable/related.md new file mode 100644 index 0000000..1e53e58 --- /dev/null +++ b/lib/common/pylint_data/messages/self-assigning-variable/related.md @@ -0,0 +1,2 @@ +- [Python assignment + statement](https://docs.python.org/3/reference/simple_stmts.html#assignment-statements) diff --git a/lib/common/pylint_data/messages/self-cls-assignment/bad.py b/lib/common/pylint_data/messages/self-cls-assignment/bad.py new file mode 100644 index 0000000..50fe890 --- /dev/null +++ b/lib/common/pylint_data/messages/self-cls-assignment/bad.py @@ -0,0 +1,9 @@ +class Fruit: + @classmethod + def list_fruits(cls): + cls = "apple" # [self-cls-assignment] + + def print_color(self, *colors): + self = "red" # [self-cls-assignment] + color = colors[1] + print(color) diff --git a/lib/common/pylint_data/messages/self-cls-assignment/good.py b/lib/common/pylint_data/messages/self-cls-assignment/good.py new file mode 100644 index 0000000..d1960f2 --- /dev/null +++ b/lib/common/pylint_data/messages/self-cls-assignment/good.py @@ -0,0 +1,9 @@ +class Fruit: + @classmethod + def list_fruits(cls): + fruit = "apple" + print(fruit) + + def print_color(self, *colors): + color = colors[1] + print(color) diff --git a/lib/common/pylint_data/messages/shadowed-import/bad.py b/lib/common/pylint_data/messages/shadowed-import/bad.py new file mode 100644 index 0000000..847ec96 --- /dev/null +++ b/lib/common/pylint_data/messages/shadowed-import/bad.py @@ -0,0 +1,3 @@ +from pathlib import Path + +import FastAPI.Path as Path # [shadowed-import] diff --git a/lib/common/pylint_data/messages/shadowed-import/good.py b/lib/common/pylint_data/messages/shadowed-import/good.py new file mode 100644 index 0000000..9f19861 --- /dev/null +++ b/lib/common/pylint_data/messages/shadowed-import/good.py @@ -0,0 +1,3 @@ +from pathlib import Path + +import FastAPI.Path as FastApiPath diff --git a/lib/common/pylint_data/messages/shallow-copy-environ/bad.py b/lib/common/pylint_data/messages/shallow-copy-environ/bad.py new file mode 100644 index 0000000..be6c020 --- /dev/null +++ b/lib/common/pylint_data/messages/shallow-copy-environ/bad.py @@ -0,0 +1,4 @@ +import copy +import os + +copied_env = copy.copy(os.environ) # [shallow-copy-environ] diff --git a/lib/common/pylint_data/messages/shallow-copy-environ/good.py b/lib/common/pylint_data/messages/shallow-copy-environ/good.py new file mode 100644 index 0000000..814ac9a --- /dev/null +++ b/lib/common/pylint_data/messages/shallow-copy-environ/good.py @@ -0,0 +1,3 @@ +import os + +copied_env = os.environ.copy() diff --git a/lib/common/pylint_data/messages/signature-differs/bad.py b/lib/common/pylint_data/messages/signature-differs/bad.py new file mode 100644 index 0000000..9a46a6f --- /dev/null +++ b/lib/common/pylint_data/messages/signature-differs/bad.py @@ -0,0 +1,9 @@ +class Animal: + def run(self, distance=0): + print(f"Ran {distance} km!") + + +class Dog(Animal): + def run(self, distance): # [signature-differs] + super(Animal, self).run(distance) + print("Fetched that stick, wuff !") diff --git a/lib/common/pylint_data/messages/signature-differs/good.py b/lib/common/pylint_data/messages/signature-differs/good.py new file mode 100644 index 0000000..6510a41 --- /dev/null +++ b/lib/common/pylint_data/messages/signature-differs/good.py @@ -0,0 +1,9 @@ +class Animal: + def run(self, distance=0): + print(f"Ran {distance} km!") + + +class Dog(Animal): + def run(self, distance=0): + super(Animal, self).run(distance) + print("Fetched that stick, wuff !") diff --git a/lib/common/pylint_data/messages/simplifiable-condition/bad.py b/lib/common/pylint_data/messages/simplifiable-condition/bad.py new file mode 100644 index 0000000..e3ffe5d --- /dev/null +++ b/lib/common/pylint_data/messages/simplifiable-condition/bad.py @@ -0,0 +1,2 @@ +def has_apples(apples) -> bool: + return bool(apples or False) # [simplifiable-condition] diff --git a/lib/common/pylint_data/messages/simplifiable-condition/good.py b/lib/common/pylint_data/messages/simplifiable-condition/good.py new file mode 100644 index 0000000..400a278 --- /dev/null +++ b/lib/common/pylint_data/messages/simplifiable-condition/good.py @@ -0,0 +1,2 @@ +def has_apples(apples) -> bool: + return bool(apples) diff --git a/lib/common/pylint_data/messages/simplifiable-if-expression/bad.py b/lib/common/pylint_data/messages/simplifiable-if-expression/bad.py new file mode 100644 index 0000000..834ee38 --- /dev/null +++ b/lib/common/pylint_data/messages/simplifiable-if-expression/bad.py @@ -0,0 +1,9 @@ +FLYING_THINGS = ["bird", "plane", "superman", "this example"] + + +def is_flying_thing(an_object): + return True if an_object in FLYING_THINGS else False # [simplifiable-if-expression] + + +def is_not_flying_thing(an_object): + return False if an_object in FLYING_THINGS else True # [simplifiable-if-expression] diff --git a/lib/common/pylint_data/messages/simplifiable-if-expression/good.py b/lib/common/pylint_data/messages/simplifiable-if-expression/good.py new file mode 100644 index 0000000..cfe7db2 --- /dev/null +++ b/lib/common/pylint_data/messages/simplifiable-if-expression/good.py @@ -0,0 +1,9 @@ +FLYING_THINGS = ["bird", "plane", "superman", "this example"] + + +def is_flying_thing(an_object): + return an_object in FLYING_THINGS + + +def is_not_flying_thing(an_object): + return an_object not in FLYING_THINGS diff --git a/lib/common/pylint_data/messages/simplifiable-if-expression/related.md b/lib/common/pylint_data/messages/simplifiable-if-expression/related.md new file mode 100644 index 0000000..c4ba9e1 --- /dev/null +++ b/lib/common/pylint_data/messages/simplifiable-if-expression/related.md @@ -0,0 +1,2 @@ +- [Simplifying an \'if\' statement with + bool()](https://stackoverflow.com/questions/49546992/) diff --git a/lib/common/pylint_data/messages/simplifiable-if-statement/bad.py b/lib/common/pylint_data/messages/simplifiable-if-statement/bad.py new file mode 100644 index 0000000..1a8d6f0 --- /dev/null +++ b/lib/common/pylint_data/messages/simplifiable-if-statement/bad.py @@ -0,0 +1,10 @@ +FLYING_THINGS = ["bird", "plane", "superman", "this example"] + + +def is_flying_animal(an_object): + # +1: [simplifiable-if-statement] + if isinstance(an_object, Animal) and an_object in FLYING_THINGS: + is_flying = True + else: + is_flying = False + return is_flying diff --git a/lib/common/pylint_data/messages/simplifiable-if-statement/good.py b/lib/common/pylint_data/messages/simplifiable-if-statement/good.py new file mode 100644 index 0000000..6669229 --- /dev/null +++ b/lib/common/pylint_data/messages/simplifiable-if-statement/good.py @@ -0,0 +1,6 @@ +FLYING_THINGS = ["bird", "plane", "superman", "this example"] + + +def is_flying_animal(an_object): + is_flying = isinstance(an_object, Animal) and an_object.name in FLYING_THINGS + return is_flying diff --git a/lib/common/pylint_data/messages/simplify-boolean-expression/bad.py b/lib/common/pylint_data/messages/simplify-boolean-expression/bad.py new file mode 100644 index 0000000..06806d3 --- /dev/null +++ b/lib/common/pylint_data/messages/simplify-boolean-expression/bad.py @@ -0,0 +1,2 @@ +def has_oranges(oranges, apples=None) -> bool: + return apples and False or oranges # [simplify-boolean-expression] diff --git a/lib/common/pylint_data/messages/simplify-boolean-expression/good.py b/lib/common/pylint_data/messages/simplify-boolean-expression/good.py new file mode 100644 index 0000000..ca1c8a2 --- /dev/null +++ b/lib/common/pylint_data/messages/simplify-boolean-expression/good.py @@ -0,0 +1,2 @@ +def has_oranges(oranges, apples=None) -> bool: + return oranges diff --git a/lib/common/pylint_data/messages/single-string-used-for-slots/bad.py b/lib/common/pylint_data/messages/single-string-used-for-slots/bad.py new file mode 100644 index 0000000..9331dd9 --- /dev/null +++ b/lib/common/pylint_data/messages/single-string-used-for-slots/bad.py @@ -0,0 +1,5 @@ +class Fruit: # [single-string-used-for-slots] + __slots__ = "name" + + def __init__(self, name): + self.name = name diff --git a/lib/common/pylint_data/messages/single-string-used-for-slots/good.py b/lib/common/pylint_data/messages/single-string-used-for-slots/good.py new file mode 100644 index 0000000..9f0ea3f --- /dev/null +++ b/lib/common/pylint_data/messages/single-string-used-for-slots/good.py @@ -0,0 +1,5 @@ +class Fruit: + __slots__ = ("name",) + + def __init__(self, name): + self.name = name diff --git a/lib/common/pylint_data/messages/singledispatch-method/bad.py b/lib/common/pylint_data/messages/singledispatch-method/bad.py new file mode 100644 index 0000000..27df8d2 --- /dev/null +++ b/lib/common/pylint_data/messages/singledispatch-method/bad.py @@ -0,0 +1,16 @@ +from functools import singledispatch + + +class Board: + @singledispatch # [singledispatch-method] + def convert_position(self, position): + pass + + @convert_position.register # [singledispatch-method] + def _(self, position: str) -> tuple: + position_a, position_b = position.split(",") + return (int(position_a), int(position_b)) + + @convert_position.register # [singledispatch-method] + def _(self, position: tuple) -> str: + return f"{position[0]},{position[1]}" diff --git a/lib/common/pylint_data/messages/singledispatch-method/details.md b/lib/common/pylint_data/messages/singledispatch-method/details.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/common/pylint_data/messages/singledispatch-method/details.md @@ -0,0 +1 @@ + diff --git a/lib/common/pylint_data/messages/singledispatch-method/good.py b/lib/common/pylint_data/messages/singledispatch-method/good.py new file mode 100644 index 0000000..36e623d --- /dev/null +++ b/lib/common/pylint_data/messages/singledispatch-method/good.py @@ -0,0 +1,17 @@ +from functools import singledispatch + + +@singledispatch +def convert_position(position): + print(position) + + +@convert_position.register +def _(position: str) -> tuple: + position_a, position_b = position.split(",") + return (int(position_a), int(position_b)) + + +@convert_position.register +def _(position: tuple) -> str: + return f"{position[0]},{position[1]}" diff --git a/lib/common/pylint_data/messages/singledispatchmethod-function/bad.py b/lib/common/pylint_data/messages/singledispatchmethod-function/bad.py new file mode 100644 index 0000000..861d3a2 --- /dev/null +++ b/lib/common/pylint_data/messages/singledispatchmethod-function/bad.py @@ -0,0 +1,17 @@ +from functools import singledispatchmethod + + +@singledispatchmethod # [singledispatchmethod-function] +def convert_position(position): + print(position) + + +@convert_position.register # [singledispatchmethod-function] +def _(position: str) -> tuple: + position_a, position_b = position.split(",") + return (int(position_a), int(position_b)) + + +@convert_position.register # [singledispatchmethod-function] +def _(position: tuple) -> str: + return f"{position[0]},{position[1]}" diff --git a/lib/common/pylint_data/messages/singledispatchmethod-function/details.md b/lib/common/pylint_data/messages/singledispatchmethod-function/details.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/common/pylint_data/messages/singledispatchmethod-function/details.md @@ -0,0 +1 @@ + diff --git a/lib/common/pylint_data/messages/singledispatchmethod-function/good.py b/lib/common/pylint_data/messages/singledispatchmethod-function/good.py new file mode 100644 index 0000000..1bc3570 --- /dev/null +++ b/lib/common/pylint_data/messages/singledispatchmethod-function/good.py @@ -0,0 +1,18 @@ +from functools import singledispatchmethod + + +class Board: + @singledispatchmethod + def convert_position(cls, position): + pass + + @singledispatchmethod + @classmethod + def _(cls, position: str) -> tuple: + position_a, position_b = position.split(",") + return (int(position_a), int(position_b)) + + @singledispatchmethod + @classmethod + def _(cls, position: tuple) -> str: + return f"{position[0]},{position[1]}" diff --git a/lib/common/pylint_data/messages/singleton-comparison/bad.py b/lib/common/pylint_data/messages/singleton-comparison/bad.py new file mode 100644 index 0000000..217a76e --- /dev/null +++ b/lib/common/pylint_data/messages/singleton-comparison/bad.py @@ -0,0 +1,3 @@ +game_won = True +if game_won == True: # [singleton-comparison] + print("Game won !") diff --git a/lib/common/pylint_data/messages/singleton-comparison/good.py b/lib/common/pylint_data/messages/singleton-comparison/good.py new file mode 100644 index 0000000..24a1370 --- /dev/null +++ b/lib/common/pylint_data/messages/singleton-comparison/good.py @@ -0,0 +1,3 @@ +game_won = True +if game_won: + print("Game won !") diff --git a/lib/common/pylint_data/messages/singleton-comparison/related.md b/lib/common/pylint_data/messages/singleton-comparison/related.md new file mode 100644 index 0000000..444d8c4 --- /dev/null +++ b/lib/common/pylint_data/messages/singleton-comparison/related.md @@ -0,0 +1 @@ +- [PEP 285 – Adding a bool type](https://peps.python.org/pep-0285/) diff --git a/lib/common/pylint_data/messages/star-needs-assignment-target/bad.py b/lib/common/pylint_data/messages/star-needs-assignment-target/bad.py new file mode 100644 index 0000000..f09102d --- /dev/null +++ b/lib/common/pylint_data/messages/star-needs-assignment-target/bad.py @@ -0,0 +1 @@ +stars = *["Sirius", "Arcturus", "Vega"] # [star-needs-assignment-target] diff --git a/lib/common/pylint_data/messages/star-needs-assignment-target/good.py b/lib/common/pylint_data/messages/star-needs-assignment-target/good.py new file mode 100644 index 0000000..867ce11 --- /dev/null +++ b/lib/common/pylint_data/messages/star-needs-assignment-target/good.py @@ -0,0 +1 @@ +sirius, *arcturus_and_vega = ["Sirius", "Arcturus", "Vega"] diff --git a/lib/common/pylint_data/messages/stop-iteration-return/bad.py b/lib/common/pylint_data/messages/stop-iteration-return/bad.py new file mode 100644 index 0000000..727adf1 --- /dev/null +++ b/lib/common/pylint_data/messages/stop-iteration-return/bad.py @@ -0,0 +1,22 @@ +def fruit_generator(): + for fruit in ["apple", "banana"]: + yield fruit + raise StopIteration # [stop-iteration-return] + +# or + +def two_fruits_generator(fruits): + for fruit in fruits: + yield fruit, next(fruits) # [stop-iteration-return] + +# or + +def two_good_fruits_generator(fruits): + for fruit in fruits: + if not fruit.is_tasty(): + continue + while True: + next_fruit = next(fruits) # [stop-iteration-return] + if next_fruit.is_tasty(): + yield fruit, next_fruit + break diff --git a/lib/common/pylint_data/messages/stop-iteration-return/details.md b/lib/common/pylint_data/messages/stop-iteration-return/details.md new file mode 100644 index 0000000..0e2a7b2 --- /dev/null +++ b/lib/common/pylint_data/messages/stop-iteration-return/details.md @@ -0,0 +1,3 @@ +It\'s possible to give a default value to `next` or catch the +`StopIteration`, or return directly. A `StopIteration` cannot be +propagated from a generator. diff --git a/lib/common/pylint_data/messages/stop-iteration-return/good.py b/lib/common/pylint_data/messages/stop-iteration-return/good.py new file mode 100644 index 0000000..282903b --- /dev/null +++ b/lib/common/pylint_data/messages/stop-iteration-return/good.py @@ -0,0 +1,34 @@ +def fruit_generator(): + """The example is simple enough you don't need an explicit return.""" + for fruit in ["apple", "banana"]: + yield fruit + +# or + +def two_fruits_generator(fruits): + """Catching the StopIteration.""" + for fruit in fruits: + try: + yield fruit, next(fruits) + except StopIteration: + print("Sorry there is only one fruit left.") + yield fruit, None + +# or + +def two_good_fruits_generator(fruits): + """A return can be used to end the iterator early, but not a StopIteration.""" + for fruit in fruits: + if not fruit.is_tasty(): + continue + while True: + next_fruit = next(fruits, None) + if next_fruit is None: + print("Sorry there is only one fruit left.") + yield fruit, None + # We reached the end of the 'fruits' generator but raising a + # StopIteration instead of returning would create a RuntimeError + return + if next_fruit.is_tasty(): + yield fruit, next_fruit + break diff --git a/lib/common/pylint_data/messages/stop-iteration-return/related.md b/lib/common/pylint_data/messages/stop-iteration-return/related.md new file mode 100644 index 0000000..5ccd909 --- /dev/null +++ b/lib/common/pylint_data/messages/stop-iteration-return/related.md @@ -0,0 +1 @@ +- [PEP 479](https://peps.python.org/pep-0479/) diff --git a/lib/common/pylint_data/messages/subclassed-final-class/bad.py b/lib/common/pylint_data/messages/subclassed-final-class/bad.py new file mode 100644 index 0000000..4d557cd --- /dev/null +++ b/lib/common/pylint_data/messages/subclassed-final-class/bad.py @@ -0,0 +1,13 @@ +from typing import final + + +@final +class PlatypusData: + """General Platypus data.""" + + average_length = 46 + average_body_temperature = 32 + + +class FluorescentPlaytipus(PlatypusData): # [subclassed-final-class] + """Playtipus with fluorescent fur.""" diff --git a/lib/common/pylint_data/messages/subclassed-final-class/details.md b/lib/common/pylint_data/messages/subclassed-final-class/details.md new file mode 100644 index 0000000..da4709a --- /dev/null +++ b/lib/common/pylint_data/messages/subclassed-final-class/details.md @@ -0,0 +1,5 @@ +This message is emitted when a class which is decorated with +[final]{.title-ref} is subclassed; the decorator indicates that the +class is not intended to be extended. + +Note this message can\'t be emitted when using Python \< 3.8. diff --git a/lib/common/pylint_data/messages/subclassed-final-class/good.py b/lib/common/pylint_data/messages/subclassed-final-class/good.py new file mode 100644 index 0000000..e8d9faa --- /dev/null +++ b/lib/common/pylint_data/messages/subclassed-final-class/good.py @@ -0,0 +1,14 @@ +from typing import final + + +@final +class PlatypusData: + """General Platypus data.""" + + average_length = 46 + average_body_temperature = 32 + + +def print_average_length_platypus(): + output = f"The average length of a platypus is: {PlatypusData.average_length}cm" + print(output) diff --git a/lib/common/pylint_data/messages/subclassed-final-class/related.md b/lib/common/pylint_data/messages/subclassed-final-class/related.md new file mode 100644 index 0000000..28c9e01 --- /dev/null +++ b/lib/common/pylint_data/messages/subclassed-final-class/related.md @@ -0,0 +1 @@ +- [PEP 591](https://peps.python.org/pep-0591/#the-final-decorator) diff --git a/lib/common/pylint_data/messages/subprocess-popen-preexec-fn/bad.py b/lib/common/pylint_data/messages/subprocess-popen-preexec-fn/bad.py new file mode 100644 index 0000000..6be75e4 --- /dev/null +++ b/lib/common/pylint_data/messages/subprocess-popen-preexec-fn/bad.py @@ -0,0 +1,8 @@ +import subprocess + + +def foo(): + pass + + +subprocess.Popen(preexec_fn=foo) # [subprocess-popen-preexec-fn] diff --git a/lib/common/pylint_data/messages/subprocess-popen-preexec-fn/good.py b/lib/common/pylint_data/messages/subprocess-popen-preexec-fn/good.py new file mode 100644 index 0000000..0cb4f6f --- /dev/null +++ b/lib/common/pylint_data/messages/subprocess-popen-preexec-fn/good.py @@ -0,0 +1,3 @@ +import subprocess + +subprocess.Popen() diff --git a/lib/common/pylint_data/messages/subprocess-run-check/bad.py b/lib/common/pylint_data/messages/subprocess-run-check/bad.py new file mode 100644 index 0000000..05e6828 --- /dev/null +++ b/lib/common/pylint_data/messages/subprocess-run-check/bad.py @@ -0,0 +1,3 @@ +import subprocess + +proc = subprocess.run(["ls"]) # [subprocess-run-check] diff --git a/lib/common/pylint_data/messages/subprocess-run-check/good.py b/lib/common/pylint_data/messages/subprocess-run-check/good.py new file mode 100644 index 0000000..1de760a --- /dev/null +++ b/lib/common/pylint_data/messages/subprocess-run-check/good.py @@ -0,0 +1,3 @@ +import subprocess + +proc = subprocess.run(["ls"], check=False) diff --git a/lib/common/pylint_data/messages/subprocess-run-check/related.md b/lib/common/pylint_data/messages/subprocess-run-check/related.md new file mode 100644 index 0000000..30c3b8d --- /dev/null +++ b/lib/common/pylint_data/messages/subprocess-run-check/related.md @@ -0,0 +1,2 @@ +- [subprocess.run + documentation](https://docs.python.org/3/library/subprocess.html#subprocess.run) diff --git a/lib/common/pylint_data/messages/super-init-not-called/bad.py b/lib/common/pylint_data/messages/super-init-not-called/bad.py new file mode 100644 index 0000000..b0e0c4c --- /dev/null +++ b/lib/common/pylint_data/messages/super-init-not-called/bad.py @@ -0,0 +1,9 @@ +class Fruit: + def __init__(self, name="fruit"): + self.name = name + print("Creating a {self.name}") + + +class Apple(Fruit): + def __init__(self): # [super-init-not-called] + print("Creating an apple") diff --git a/lib/common/pylint_data/messages/super-init-not-called/good.py b/lib/common/pylint_data/messages/super-init-not-called/good.py new file mode 100644 index 0000000..7f8a177 --- /dev/null +++ b/lib/common/pylint_data/messages/super-init-not-called/good.py @@ -0,0 +1,9 @@ +class Fruit: + def __init__(self, name="fruit"): + self.name = name + print("Creating a {self.name}") + + +class Apple(Fruit): + def __init__(self): + super().__init__("apple") diff --git a/lib/common/pylint_data/messages/super-with-arguments/bad.py b/lib/common/pylint_data/messages/super-with-arguments/bad.py new file mode 100644 index 0000000..a5adf26 --- /dev/null +++ b/lib/common/pylint_data/messages/super-with-arguments/bad.py @@ -0,0 +1,7 @@ +class Fruit: + pass + + +class Orange(Fruit): + def __init__(self): + super(Orange, self).__init__() # [super-with-arguments] diff --git a/lib/common/pylint_data/messages/super-with-arguments/good.py b/lib/common/pylint_data/messages/super-with-arguments/good.py new file mode 100644 index 0000000..d925fab --- /dev/null +++ b/lib/common/pylint_data/messages/super-with-arguments/good.py @@ -0,0 +1,7 @@ +class Fruit: + pass + + +class Orange(Fruit): + def __init__(self): + super().__init__() diff --git a/lib/common/pylint_data/messages/super-without-brackets/bad.py b/lib/common/pylint_data/messages/super-without-brackets/bad.py new file mode 100644 index 0000000..19dcb12 --- /dev/null +++ b/lib/common/pylint_data/messages/super-without-brackets/bad.py @@ -0,0 +1,11 @@ +class Soup: + @staticmethod + def temp(): + print("Soup is hot!") + + +class TomatoSoup(Soup): + @staticmethod + def temp(): + super.temp() # [super-without-brackets] + print("But tomato soup is even hotter!") diff --git a/lib/common/pylint_data/messages/super-without-brackets/good.py b/lib/common/pylint_data/messages/super-without-brackets/good.py new file mode 100644 index 0000000..3dadd11 --- /dev/null +++ b/lib/common/pylint_data/messages/super-without-brackets/good.py @@ -0,0 +1,11 @@ +class Soup: + @staticmethod + def temp(): + print("Soup is hot!") + + +class TomatoSoup(Soup): + @staticmethod + def temp(): + super().temp() + print("But tomato soup is even hotter!") diff --git a/lib/common/pylint_data/messages/superfluous-parens/bad.py b/lib/common/pylint_data/messages/superfluous-parens/bad.py new file mode 100644 index 0000000..0c4ced4 --- /dev/null +++ b/lib/common/pylint_data/messages/superfluous-parens/bad.py @@ -0,0 +1,11 @@ +# Example 1 +x = input() +y = input() +if (x == y): # [superfluous-parens] + pass + +# Example 2 +i = 0 +exclude = [] +if (i - 0) in exclude: # [superfluous-parens] + pass diff --git a/lib/common/pylint_data/messages/superfluous-parens/good.py b/lib/common/pylint_data/messages/superfluous-parens/good.py new file mode 100644 index 0000000..a155c6d --- /dev/null +++ b/lib/common/pylint_data/messages/superfluous-parens/good.py @@ -0,0 +1,11 @@ +# Example 1 +x = input() +y = input() +if x == y: + pass + +# Example 2 +i = 0 +exclude = [] +if i - 0 in exclude: + pass diff --git a/lib/common/pylint_data/messages/suppressed-message/bad.py b/lib/common/pylint_data/messages/suppressed-message/bad.py new file mode 100644 index 0000000..5b4c437 --- /dev/null +++ b/lib/common/pylint_data/messages/suppressed-message/bad.py @@ -0,0 +1,9 @@ +### This is a contrived example, to show how suppressed-message works. +### First we enable all messages +# pylint: enable=all + +### Here we disable two messages so we get two warnings +# pylint: disable=locally-disabled, useless-suppression # [suppressed-message, suppressed-message] + +### Here we disable a message, so we get a warning for suppressed-message again. +"A" # pylint: disable=pointless-statement # [suppressed-message, suppressed-message] diff --git a/lib/common/pylint_data/messages/suppressed-message/details.md b/lib/common/pylint_data/messages/suppressed-message/details.md new file mode 100644 index 0000000..044a638 --- /dev/null +++ b/lib/common/pylint_data/messages/suppressed-message/details.md @@ -0,0 +1,4 @@ +`suppressed-message` is simply a way to see messages that would be +raised without the disable in your codebase. It should not be activated +most of the time. See also `useless-suppression` if you want to see the +message that are disabled for no reasons. diff --git a/lib/common/pylint_data/messages/suppressed-message/good.py b/lib/common/pylint_data/messages/suppressed-message/good.py new file mode 100644 index 0000000..39c3624 --- /dev/null +++ b/lib/common/pylint_data/messages/suppressed-message/good.py @@ -0,0 +1 @@ +"""Instead of a single string somewhere in the file, write a module docstring!""" diff --git a/lib/common/pylint_data/messages/syntax-error/bad.py b/lib/common/pylint_data/messages/syntax-error/bad.py new file mode 100644 index 0000000..6a34478 --- /dev/null +++ b/lib/common/pylint_data/messages/syntax-error/bad.py @@ -0,0 +1,5 @@ +fruit_stock = { + 'apple': 42, + 'orange': 21 # [syntax-error] + 'banana': 12 +} diff --git a/lib/common/pylint_data/messages/syntax-error/details.md b/lib/common/pylint_data/messages/syntax-error/details.md new file mode 100644 index 0000000..c58fc91 --- /dev/null +++ b/lib/common/pylint_data/messages/syntax-error/details.md @@ -0,0 +1,3 @@ +The python\'s ast builtin module cannot parse your code if there\'s a +syntax error, so if there\'s a syntax error other messages won\'t be +available at all. diff --git a/lib/common/pylint_data/messages/syntax-error/good.py b/lib/common/pylint_data/messages/syntax-error/good.py new file mode 100644 index 0000000..10a644f --- /dev/null +++ b/lib/common/pylint_data/messages/syntax-error/good.py @@ -0,0 +1 @@ +fruit_stock = {"apple": 42, "orange": 21, "banana": 12} diff --git a/lib/common/pylint_data/messages/syntax-error/related.md b/lib/common/pylint_data/messages/syntax-error/related.md new file mode 100644 index 0000000..afae4e8 --- /dev/null +++ b/lib/common/pylint_data/messages/syntax-error/related.md @@ -0,0 +1,2 @@ +- [Why can\'t pylint recover from a syntax error + ?](https://stackoverflow.com/a/78419051/2519059) diff --git a/lib/common/pylint_data/messages/too-complex/bad.py b/lib/common/pylint_data/messages/too-complex/bad.py new file mode 100644 index 0000000..8d6d78b --- /dev/null +++ b/lib/common/pylint_data/messages/too-complex/bad.py @@ -0,0 +1,44 @@ +def fifty_percent_off(whole): + return (float(whole)) * 50 / 100 + + +def calculate_sum_and_display_price_of_fruits(*fruits): # [too-complex] + # McCabe rating is 13 here (by default 10) + shopping_list = [] + + if "apple" in fruits: + v = fifty_percent_off(1.1) + shopping_list.append(v) + if "pear" in fruits: + shopping_list.append(0.8) + if "banana" in fruits: + shopping_list.append(1.2) + if "mango" in fruits: + shopping_list.append(3.5) + if "peach" in fruits: + shopping_list.append(0.5) + if "melon" in fruits: + shopping_list.append(4.9) + if "orange" in fruits: + shopping_list.append(2.0) + if "strawberry" in fruits: + shopping_list.append(2.5) + if "mandarin" in fruits: + shopping_list.append(2.3) + if "plum" in fruits: + shopping_list.append(0.5) + if "watermelon" in fruits: + v = fifty_percent_off(6.4) + shopping_list.append(v) + + combine = zip(fruits, shopping_list) + + for i in combine: + print(f"{i[0]} ${i[1]:.2f}") + + total = sum(shopping_list) + print(f"Total price is ${total:.2f}") + + +fruits_to_buy = ["apple", "orange", "watermelon"] +calculate_sum_and_display_price_of_fruits(*fruits_to_buy) diff --git a/lib/common/pylint_data/messages/too-complex/good.py b/lib/common/pylint_data/messages/too-complex/good.py new file mode 100644 index 0000000..77711f7 --- /dev/null +++ b/lib/common/pylint_data/messages/too-complex/good.py @@ -0,0 +1,40 @@ +FRUIT_PRICES = { + "apple": 1.1, + "pear": 0.8, + "banana": 1.2, + "mango": 3.5, + "peach": 0.5, + "melon": 4.9, + "orange": 2.0, + "strawberry": 2.5, + "mandarin": 2.3, + "plum": 0.5, + "watermelon": 6.4, +} +DISCOUNTED_FRUITS = ["apple", "watermelon"] + + +def fifty_percent_off(whole): + return (float(whole)) * 50 / 100 + + +def get_price(fruit): + full_price = FRUIT_PRICES.get(fruit) + if fruit in DISCOUNTED_FRUITS: + return fifty_percent_off(full_price) + else: + return full_price + + +def display_fruit_and_price(fruits): + for fruit in fruits: + print(f"{fruit} ${get_price(fruit) :.2f}") + + +def get_total(fruits): + return sum(get_price(f) for f in fruits) + + +fruits_to_buy = ["apple", "orange", "watermelon"] +display_fruit_and_price(fruits_to_buy) +print(f"Total price is ${get_total(fruits_to_buy):.2f}") diff --git a/lib/common/pylint_data/messages/too-few-format-args/bad.py b/lib/common/pylint_data/messages/too-few-format-args/bad.py new file mode 100644 index 0000000..03fa3de --- /dev/null +++ b/lib/common/pylint_data/messages/too-few-format-args/bad.py @@ -0,0 +1 @@ +print("Today is {0}, so tomorrow will be {1}".format("Monday")) # [too-few-format-args] diff --git a/lib/common/pylint_data/messages/too-few-format-args/good.py b/lib/common/pylint_data/messages/too-few-format-args/good.py new file mode 100644 index 0000000..c6b2a48 --- /dev/null +++ b/lib/common/pylint_data/messages/too-few-format-args/good.py @@ -0,0 +1 @@ +print("Today is {0}, so tomorrow will be {1}".format("Monday", "Tuesday")) diff --git a/lib/common/pylint_data/messages/too-few-format-args/related.md b/lib/common/pylint_data/messages/too-few-format-args/related.md new file mode 100644 index 0000000..cb12e54 --- /dev/null +++ b/lib/common/pylint_data/messages/too-few-format-args/related.md @@ -0,0 +1,2 @@ +- [String + Formatting](https://docs.python.org/3/library/string.html#formatstrings) diff --git a/lib/common/pylint_data/messages/too-few-public-methods/bad.py b/lib/common/pylint_data/messages/too-few-public-methods/bad.py new file mode 100644 index 0000000..bc8748f --- /dev/null +++ b/lib/common/pylint_data/messages/too-few-public-methods/bad.py @@ -0,0 +1,7 @@ +class Worm: # [too-few-public-methods] + def __init__(self, name: str, fruit_of_residence: Fruit): + self.name = name + self.fruit_of_residence = fruit_of_residence + + def bore(self): + print(f"{self.name} is boring into {self.fruit_of_residence}") diff --git a/lib/common/pylint_data/messages/too-few-public-methods/good.py b/lib/common/pylint_data/messages/too-few-public-methods/good.py new file mode 100644 index 0000000..1fe1424 --- /dev/null +++ b/lib/common/pylint_data/messages/too-few-public-methods/good.py @@ -0,0 +1,28 @@ +# Function example +def bore(fruit: Fruit, worm_name: str): + print(f"{worm_name} is boring into {fruit}") + +# Dataclass and Function example +import dataclasses + + +@dataclasses.dataclass +class Worm: + name: str + fruit_of_residence: Fruit + + +def bore(worm: Worm): + print(f"{worm.name} is boring into {worm.fruit_of_residence}") + +# A Larger API example +class Worm: + def __init__(self, name: str, fruit_of_residence: Fruit): + self.name = name + self.fruit_of_residence = fruit_of_residence + + def bore(self): + print(f"{self.name} is boring into {self.fruit_of_residence}") + + def wiggle(self): + print(f"{self.name} wiggle around wormily.") diff --git a/lib/common/pylint_data/messages/too-many-ancestors/bad.py b/lib/common/pylint_data/messages/too-many-ancestors/bad.py new file mode 100644 index 0000000..dfe70b8 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-ancestors/bad.py @@ -0,0 +1,24 @@ +class Animal: ... +class BeakyAnimal(Animal): ... +class FurryAnimal(Animal): ... +class Swimmer(Animal): ... +class EggLayer(Animal): ... +class VenomousAnimal(Animal): ... +class ProtectedSpecie(Animal): ... +class BeaverTailedAnimal(Animal): ... +class Vertebrate(Animal): ... + + +# max of 7 by default, can be configured +# each edge of a diamond inheritance counts +class Playtypus( # [too-many-ancestors] + BeakyAnimal, + FurryAnimal, + Swimmer, + EggLayer, + VenomousAnimal, + ProtectedSpecie, + BeaverTailedAnimal, + Vertebrate, +): + pass diff --git a/lib/common/pylint_data/messages/too-many-ancestors/good.py b/lib/common/pylint_data/messages/too-many-ancestors/good.py new file mode 100644 index 0000000..ea6003c --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-ancestors/good.py @@ -0,0 +1,33 @@ +class Animal: + beaver_tailed: bool + can_swim: bool + has_beak: bool + has_fur: bool + has_vertebrae: bool + lays_egg: bool + protected_specie: bool + venomous: bool + + +class Invertebrate(Animal): + has_vertebrae = False + + +class Vertebrate(Animal): + has_vertebrae = True + + +class Mammal(Vertebrate): + has_beak = False + has_fur = True + lays_egg = False + venomous = False + + +class Playtypus(Mammal): + beaver_tailed = True + can_swim = True + has_beak = True + lays_egg = True + protected_specie = True + venomous = True diff --git a/lib/common/pylint_data/messages/too-many-arguments/bad.py b/lib/common/pylint_data/messages/too-many-arguments/bad.py new file mode 100644 index 0000000..5f8d6e0 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-arguments/bad.py @@ -0,0 +1,16 @@ +def three_d_chess_move( # [too-many-arguments] + x_white, + y_white, + z_white, + piece_white, + x_black, + y_black, + z_black, + piece_black, + x_blue, + y_blue, + z_blue, + piece_blue, + current_player, +): + pass diff --git a/lib/common/pylint_data/messages/too-many-arguments/good.py b/lib/common/pylint_data/messages/too-many-arguments/good.py new file mode 100644 index 0000000..f3ea3c8 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-arguments/good.py @@ -0,0 +1,18 @@ +from dataclasses import dataclass + + +@dataclass +class ThreeDChessPiece: + x: int + y: int + z: int + type: str + + +def three_d_chess_move( + white: ThreeDChessPiece, + black: ThreeDChessPiece, + blue: ThreeDChessPiece, + current_player, +): + pass diff --git a/lib/common/pylint_data/messages/too-many-boolean-expressions/bad.py b/lib/common/pylint_data/messages/too-many-boolean-expressions/bad.py new file mode 100644 index 0000000..5f3ae41 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-boolean-expressions/bad.py @@ -0,0 +1,5 @@ +def can_be_divided_by_two_and_are_not_zero(x, y, z): + # Maximum number of boolean expressions in an if statement (by default 5) + # +1: [too-many-boolean-expressions] + if (x and y and z) and (x % 2 == 0 and y % 2 == 0 and z % 2 == 0): + pass diff --git a/lib/common/pylint_data/messages/too-many-boolean-expressions/good.py b/lib/common/pylint_data/messages/too-many-boolean-expressions/good.py new file mode 100644 index 0000000..8405d4f --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-boolean-expressions/good.py @@ -0,0 +1,3 @@ +def can_be_divided_by_two_and_are_not_zero(x, y, z): + if all(i and i % 2 == 0 for i in [x, y, z]): + pass diff --git a/lib/common/pylint_data/messages/too-many-branches/bad.py b/lib/common/pylint_data/messages/too-many-branches/bad.py new file mode 100644 index 0000000..d3fe8f4 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-branches/bad.py @@ -0,0 +1,23 @@ +def num_to_word(x): # [too-many-branches] + if x == 0: + return "zero" + elif x == 1: + return "one" + elif x == 2: + return "two" + elif x == 3: + return "three" + elif x == 4: + return "four" + elif x == 5: + return "five" + elif x == 6: + return "six" + elif x == 7: + return "seven" + elif x == 8: + return "eight" + elif x == 9: + return "nine" + else: + return None diff --git a/lib/common/pylint_data/messages/too-many-branches/good.py b/lib/common/pylint_data/messages/too-many-branches/good.py new file mode 100644 index 0000000..10932ad --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-branches/good.py @@ -0,0 +1,13 @@ +def num_to_word(x): + return { + 0: "zero", + 1: "one", + 2: "two", + 3: "three", + 4: "four", + 5: "five", + 6: "six", + 7: "seven", + 8: "eight", + 9: "nine", + }.get(x) diff --git a/lib/common/pylint_data/messages/too-many-format-args/bad.py b/lib/common/pylint_data/messages/too-many-format-args/bad.py new file mode 100644 index 0000000..548f410 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-format-args/bad.py @@ -0,0 +1,2 @@ +# +1: [too-many-format-args] +print("Today is {0}, so tomorrow will be {1}".format("Monday", "Tuesday", "Wednesday")) diff --git a/lib/common/pylint_data/messages/too-many-format-args/good.py b/lib/common/pylint_data/messages/too-many-format-args/good.py new file mode 100644 index 0000000..c6b2a48 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-format-args/good.py @@ -0,0 +1 @@ +print("Today is {0}, so tomorrow will be {1}".format("Monday", "Tuesday")) diff --git a/lib/common/pylint_data/messages/too-many-format-args/related.md b/lib/common/pylint_data/messages/too-many-format-args/related.md new file mode 100644 index 0000000..cb12e54 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-format-args/related.md @@ -0,0 +1,2 @@ +- [String + Formatting](https://docs.python.org/3/library/string.html#formatstrings) diff --git a/lib/common/pylint_data/messages/too-many-function-args/bad.py b/lib/common/pylint_data/messages/too-many-function-args/bad.py new file mode 100644 index 0000000..97eedb9 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-function-args/bad.py @@ -0,0 +1,6 @@ +class Fruit: + def __init__(self, color): + self.color = color + + +apple = Fruit("red", "apple", [1, 2, 3]) # [too-many-function-args] diff --git a/lib/common/pylint_data/messages/too-many-function-args/good.py b/lib/common/pylint_data/messages/too-many-function-args/good.py new file mode 100644 index 0000000..338b8e1 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-function-args/good.py @@ -0,0 +1,7 @@ +class Fruit: + def __init__(self, color, name): + self.color = color + self.name = name + + +apple = Fruit("red", "apple") diff --git a/lib/common/pylint_data/messages/too-many-instance-attributes/bad.py b/lib/common/pylint_data/messages/too-many-instance-attributes/bad.py new file mode 100644 index 0000000..0892a08 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-instance-attributes/bad.py @@ -0,0 +1,13 @@ +class Fruit: # [too-many-instance-attributes] + def __init__(self): + # max of 7 attributes by default, can be configured + self.worm_name = "Jimmy" + self.worm_type = "Codling Moths" + self.worm_color = "light brown" + self.fruit_name = "Little Apple" + self.fruit_color = "Bright red" + self.fruit_vitamins = ["A", "B1"] + self.fruit_antioxidants = None + self.secondary_worm_name = "Kim" + self.secondary_worm_type = "Apple maggot" + self.secondary_worm_color = "Whitish" diff --git a/lib/common/pylint_data/messages/too-many-instance-attributes/good.py b/lib/common/pylint_data/messages/too-many-instance-attributes/good.py new file mode 100644 index 0000000..0863070 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-instance-attributes/good.py @@ -0,0 +1,20 @@ +import dataclasses + + +@dataclasses.dataclass +class Worm: + name: str + type: str + color: str + + +class Fruit: + def __init__(self): + self.name = "Little Apple" + self.color = "Bright red" + self.vitamins = ["A", "B1"] + self.antioxidants = None + self.worms = [ + Worm(name="Jimmy", type="Codling Moths", color="light brown"), + Worm(name="Kim", type="Apple maggot", color="Whitish"), + ] diff --git a/lib/common/pylint_data/messages/too-many-lines/bad.py b/lib/common/pylint_data/messages/too-many-lines/bad.py new file mode 100644 index 0000000..ef3ef11 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-lines/bad.py @@ -0,0 +1,16 @@ +def is_palindrome(string): # [too-many-lines] + left_pos = 0 + right_pos = len(string) - 1 + while right_pos >= left_pos: + if not string[left_pos] == string[right_pos]: + return False + left_pos += 1 + right_pos -= 1 + return True + + +def main(): + print(is_palindrome("aza")) + print(is_palindrome("racecar")) + print(is_palindrome("trigger")) + print(is_palindrome("ogre")) diff --git a/lib/common/pylint_data/messages/too-many-lines/details.md b/lib/common/pylint_data/messages/too-many-lines/details.md new file mode 100644 index 0000000..8e28b1b --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-lines/details.md @@ -0,0 +1,19 @@ +When a module has too many lines it can make it difficult to read and +understand. There might be performance issue while editing the file +because the IDE must parse more code. You need more expertise to +navigate the file properly (go to a particular line when debugging, or +search for a specific code construct, instead of navigating by clicking +and scrolling) + +This measure is a proxy for higher cyclomatic complexity that you might +not be calculating if you\'re not using +`load-plugins=pylint.extensions.mccabe,`. Cyclomatic complexity is +slower to compute, but also a more fine grained measure than raw SLOC. +In particular, you can\'t make the code less readable by making a very +complex one liner if you\'re using cyclomatic complexity. + +The example simplify the code, but it\'s not always possible. Most of +the time bursting the file by creating a package with the same API is +the only solution. Anticipating and creating the file from the get go +will permit to have the same end result with a better version control +history. diff --git a/lib/common/pylint_data/messages/too-many-lines/good.py b/lib/common/pylint_data/messages/too-many-lines/good.py new file mode 100644 index 0000000..0489d24 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-lines/good.py @@ -0,0 +1,7 @@ +def is_palindrome(string): + return string == string[::-1] + + +def main(): + for string in ["aza", "racecar", "trigger", "ogre"]: # [loop used instead of separate lines] + print(is_palindrome(string)) diff --git a/lib/common/pylint_data/messages/too-many-locals/bad.py b/lib/common/pylint_data/messages/too-many-locals/bad.py new file mode 100644 index 0000000..d2a762b --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-locals/bad.py @@ -0,0 +1,29 @@ +from childhood import Child, Sweet + + +def handle_sweets(infos): # [too-many-locals] + # Create children + children = [Child(info) for info in infos] + number_of_sweets = 87 + sweets = [Sweet() * number_of_sweets] + number_of_sweet_per_child = 5 + money = 45.0 + sweets_given = 0 + time_to_eat_sweet = 54 + price_of_sweet = 0.42 + # distribute sweet + for child in children: + sweets_given += number_of_sweet_per_child + child.give(sweets[number_of_sweet_per_child:]) + # calculate prices + cost_of_children = sweets_given * price_of_sweet + # Calculate remaining money + remaining_money = money - cost_of_children + # Calculate time it took + time_it_took_assuming_parallel_eating = ( + time_to_eat_sweet * number_of_sweet_per_child + ) + print( + f"{children} ate {cost_of_children}¤ of sweets in {time_it_took_assuming_parallel_eating}, " + f"you still have {remaining_money}" + ) diff --git a/lib/common/pylint_data/messages/too-many-locals/details.md b/lib/common/pylint_data/messages/too-many-locals/details.md new file mode 100644 index 0000000..3bfd9a0 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-locals/details.md @@ -0,0 +1,4 @@ +Having too many locals may indicate that you\'re doing too much in a +function and that classes regrouping some attributes could be created. +Maybe operations could be separated in multiple functions. Are all your +variables really closely related ? diff --git a/lib/common/pylint_data/messages/too-many-locals/good.py b/lib/common/pylint_data/messages/too-many-locals/good.py new file mode 100644 index 0000000..c1e7420 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-locals/good.py @@ -0,0 +1,44 @@ +from typing import NamedTuple + +from childhood import Child, Sweet + + +class SweetDistrubutionCharacteristics(NamedTuple): + number_of_sweets: int + number_of_sweet_per_child: int + number_of_children: int + + @property + def sweets_given(self): + return self.number_of_sweet_per_child * self.number_of_children + + +def handle_sweets(infos): + children = [Child(info) for info in infos] + characteristics = SweetDistrubutionCharacteristics(87, 5, len(children)) + _allocate_sweets_to_children(children, characteristics) + financial_impact = _assess_financial_impact(characteristics) + print(f"{children} ate {financial_impact}") + + +def _allocate_sweets_to_children( + children, characteristics: SweetDistrubutionCharacteristics +) -> None: + sweets = [Sweet() * characteristics.number_of_sweets] + for child in children: + child.give(sweets[characteristics.number_of_sweet_per_child :]) + + +def _assess_financial_impact(characteristics: SweetDistrubutionCharacteristics) -> str: + time_to_eat_sweet = 54 + money = 45.0 + price_of_sweet = 0.42 + cost_of_children = characteristics.sweets_given * price_of_sweet + remaining_money = money - cost_of_children + time_it_took_assuming_parallel_eating = ( + time_to_eat_sweet * characteristics.number_of_sweet_per_child + ) + return ( + f"{cost_of_children}¤ of sweets in " + f"{time_it_took_assuming_parallel_eating}, you still have {remaining_money}" + ) diff --git a/lib/common/pylint_data/messages/too-many-nested-blocks/bad.py b/lib/common/pylint_data/messages/too-many-nested-blocks/bad.py new file mode 100644 index 0000000..8a2c79e --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-nested-blocks/bad.py @@ -0,0 +1,10 @@ +def correct_fruits(fruits): + if len(fruits) > 1: # [too-many-nested-blocks] + if "apple" in fruits: + if "orange" in fruits: + count = fruits["orange"] + if count % 2: + if "kiwi" in fruits: + if count == 2: + return True + return False diff --git a/lib/common/pylint_data/messages/too-many-nested-blocks/good.py b/lib/common/pylint_data/messages/too-many-nested-blocks/good.py new file mode 100644 index 0000000..1b14770 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-nested-blocks/good.py @@ -0,0 +1,6 @@ +def correct_fruits(fruits): + if len(fruits) > 1 and "apple" in fruits and "orange" in fruits: + count = fruits["orange"] + if count % 2 and "kiwi" in fruits and count == 2: + return True + return False diff --git a/lib/common/pylint_data/messages/too-many-positional-arguments/bad.py b/lib/common/pylint_data/messages/too-many-positional-arguments/bad.py new file mode 100644 index 0000000..3cce780 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-positional-arguments/bad.py @@ -0,0 +1,7 @@ +# +1: [too-many-positional-arguments] +def calculate_drag_force(velocity, area, density, drag_coefficient): + """Each argument is positional-or-keyword unless a `/` or `*` is present.""" + return 0.5 * drag_coefficient * density * area * velocity**2 + + +drag_force = calculate_drag_force(30, 2.5, 1.225, 0.47) diff --git a/lib/common/pylint_data/messages/too-many-positional-arguments/details.md b/lib/common/pylint_data/messages/too-many-positional-arguments/details.md new file mode 100644 index 0000000..5ccb292 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-positional-arguments/details.md @@ -0,0 +1,12 @@ +Good function signatures don't have many positional parameters. For +almost all interfaces, comprehensibility suffers beyond a handful of +arguments. + +Positional arguments work well for cases where the use cases are +self-evident, such as unittest\'s +`assertEqual(first, second, "assert msg")` or `zip(fruits, vegetables)`. + +There are a few exceptions where four or more positional parameters make +sense, for example `rgba(1.0, 0.5, 0.3, 1.0)`, because it uses a very +well-known and well-established convention, and using keywords all the +time would be a waste of time. diff --git a/lib/common/pylint_data/messages/too-many-positional-arguments/good.py b/lib/common/pylint_data/messages/too-many-positional-arguments/good.py new file mode 100644 index 0000000..0e232e1 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-positional-arguments/good.py @@ -0,0 +1,14 @@ +def calculate_drag_force(*, velocity, area, density, drag_coefficient): + """This function is 'Keyword only' for all args due to the '*'.""" + return 0.5 * drag_coefficient * density * area * velocity**2 + + +# This is now impossible to do and will raise a TypeError: +# drag_force = calculate_drag_force(30, 2.5, 1.225, 0.47) +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# TypeError: calculate_drag_force() takes 0 positional arguments but 4 were given + +# And this is the only way to call 'calculate_drag_force' +drag_force = calculate_drag_force( + velocity=30, area=2.5, density=1.225, drag_coefficient=0.47 +) diff --git a/lib/common/pylint_data/messages/too-many-positional-arguments/related.md b/lib/common/pylint_data/messages/too-many-positional-arguments/related.md new file mode 100644 index 0000000..00101b4 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-positional-arguments/related.md @@ -0,0 +1,2 @@ +- [Special parameters in + python](https://docs.python.org/3/tutorial/controlflow.html#special-parameters) diff --git a/lib/common/pylint_data/messages/too-many-positional-sub-patterns/bad.py b/lib/common/pylint_data/messages/too-many-positional-sub-patterns/bad.py new file mode 100644 index 0000000..2786d41 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-positional-sub-patterns/bad.py @@ -0,0 +1,13 @@ +class Book: + __match_args__ = ("title", "year") + + def __init__(self, title, year, author): + self.title = title + self.year = year + self.author = author + + +def func(item: Book): + match item: + case Book("title", 2000, "author"): # [too-many-positional-sub-patterns] + ... diff --git a/lib/common/pylint_data/messages/too-many-positional-sub-patterns/good.py b/lib/common/pylint_data/messages/too-many-positional-sub-patterns/good.py new file mode 100644 index 0000000..6565176 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-positional-sub-patterns/good.py @@ -0,0 +1,13 @@ +class Book: + __match_args__ = ("title", "year") + + def __init__(self, title, year, author): + self.title = title + self.year = year + self.author = author + + +def func(item: Book): + match item: + case Book("title", 2000, author="author"): + ... diff --git a/lib/common/pylint_data/messages/too-many-positional-sub-patterns/related.md b/lib/common/pylint_data/messages/too-many-positional-sub-patterns/related.md new file mode 100644 index 0000000..6c207f8 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-positional-sub-patterns/related.md @@ -0,0 +1,2 @@ +- [Python + documentation](https://docs.python.org/3/reference/compound_stmts.html#class-patterns) diff --git a/lib/common/pylint_data/messages/too-many-public-methods/bad.py b/lib/common/pylint_data/messages/too-many-public-methods/bad.py new file mode 100644 index 0000000..5536513 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-public-methods/bad.py @@ -0,0 +1,33 @@ +class SpaceInvaders: # [too-many-public-methods] + def __init__(self): + pass + + def fire_laser_beam(self): + pass + + def deploy_shield(self): + pass + + def launch_missile(self): + pass + + def activate_super_laser(self): + pass + + def summon_mothership(self): + pass + + def destroy_planet(self): + pass + + def teleport(self): + pass + + def invoke_aliens(self): + pass + + def invade_earth(self): + pass + + def takeover_galaxy(self): + pass diff --git a/lib/common/pylint_data/messages/too-many-public-methods/details.md b/lib/common/pylint_data/messages/too-many-public-methods/details.md new file mode 100644 index 0000000..3cb806a --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-public-methods/details.md @@ -0,0 +1,6 @@ +Having too many public methods is an indication that you might not be +respecting the Single-responsibility principle (S of SOLID). + +The class should have only one reason to change, but in the example the +spaceship has at least 4 persons that could ask for change to it (laser +manager, shield manager, missile manager, teleportation officer\...). diff --git a/lib/common/pylint_data/messages/too-many-public-methods/good.py b/lib/common/pylint_data/messages/too-many-public-methods/good.py new file mode 100644 index 0000000..196fcbe --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-public-methods/good.py @@ -0,0 +1,47 @@ +class LaserBeam: + def __init__(self): + pass + + def fire(self): + pass + + def activate_super(self): + pass + + def destroy_planet(self): + pass + + +class Shield: + def deploy(self): + pass + + +class Missile: + def launch(self): + pass + + +class SpaceInvaders: + def __init__(self): + self.laser = LaserBeam() + self.shield = Shield() + self.missile = Missile() + + def summon_mothership(self): + pass + + def destroy_planet(self): + pass + + def teleport(self): + pass + + def invoke_aliens(self): + pass + + def invade_earth(self): + pass + + def takeover_galaxy(self): + pass diff --git a/lib/common/pylint_data/messages/too-many-public-methods/related.md b/lib/common/pylint_data/messages/too-many-public-methods/related.md new file mode 100644 index 0000000..1f8fa95 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-public-methods/related.md @@ -0,0 +1,2 @@ +- [Single-responsibility + principle](https://en.wikipedia.org/wiki/Single-responsibility_principle) diff --git a/lib/common/pylint_data/messages/too-many-return-statements/bad.py b/lib/common/pylint_data/messages/too-many-return-statements/bad.py new file mode 100644 index 0000000..827177f --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-return-statements/bad.py @@ -0,0 +1,16 @@ +def to_string(x): # [too-many-return-statements] + # max of 6 by default, can be configured + if x == 1: + return "This is one." + if x == 2: + return "This is two." + if x == 3: + return "This is three." + if x == 4: + return "This is four." + if x == 5: + return "This is five." + if x == 6: + return "This is six." + if x == 7: + return "This is seven." diff --git a/lib/common/pylint_data/messages/too-many-return-statements/good.py b/lib/common/pylint_data/messages/too-many-return-statements/good.py new file mode 100644 index 0000000..80ae2ac --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-return-statements/good.py @@ -0,0 +1,13 @@ +NUMBERS_TO_STRINGS = { + 1: "one", + 2: "two", + 3: "three", + 4: "four", + 5: "five", + 6: "six", + 7: "seven", +} + + +def to_string(x): + return f"This is {NUMBERS_TO_STRINGS.get(x)}." diff --git a/lib/common/pylint_data/messages/too-many-star-expressions/bad.py b/lib/common/pylint_data/messages/too-many-star-expressions/bad.py new file mode 100644 index 0000000..e8443c8 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-star-expressions/bad.py @@ -0,0 +1 @@ +*stars, *constellations = ["Sirius", "Arcturus", "Vega"] # [too-many-star-expressions] diff --git a/lib/common/pylint_data/messages/too-many-star-expressions/good.py b/lib/common/pylint_data/messages/too-many-star-expressions/good.py new file mode 100644 index 0000000..73f0970 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-star-expressions/good.py @@ -0,0 +1 @@ +*sirius_and_arcturus, vega = ["Sirius", "Arcturus", "Vega"] diff --git a/lib/common/pylint_data/messages/too-many-statements/bad.py b/lib/common/pylint_data/messages/too-many-statements/bad.py new file mode 100644 index 0000000..11c61c2 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-statements/bad.py @@ -0,0 +1,32 @@ +import random + + +def distribute_candies( # [too-many-statements] + children: list[Child], candies_per_child: int +): + # This function is a masterpiece of code that embodies the epitome of efficiency + # it's also an essential part of a high-priority project with extremely tight deadlines + # and there is absolutely no time to refactor it to make it more concise. + # The lead developer on the project, who has decades of experience, + # has personally reviewed this implementation and deemed it good enough as it is. + # The person writing this code has a demanding job and multiple responsibilities, + # and simply does not have the luxury of spending time making this code more readable. + total_candies = len(children) * candies_per_child + eaten_candies = 0 + # Counting candies given to each child + for child in children: + # If a child eat more than 1 candies they're going to eat all + # the candies for sure + eaten_for_child = random.choices([0, 1, candies_per_child]) + print( + f"Child {child} gets {candies_per_child} candies and eat {eaten_for_child}" + ) + remaining_candies_for_children = child.eat_candies(eaten_for_child) + if remaining_candies_for_children == 0: + print(f"All the candies have been devoured by {child.name}!") + else: + print( + f"{child.name} still have {remaining_candies_for_children} candies left." + ) + eaten_candies += eaten_for_child + return eaten_candies, total_candies diff --git a/lib/common/pylint_data/messages/too-many-statements/good.py b/lib/common/pylint_data/messages/too-many-statements/good.py new file mode 100644 index 0000000..4736fdc --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-statements/good.py @@ -0,0 +1,22 @@ +import random + + +def distribute_candies(children: list[Child], candies_per_child: int): + total_candies = len(children) * candies_per_child + eaten_candies = 0 + for child in children: + eaten_candies += _distribute_candies_to_child(candies_per_child, child) + return eaten_candies, total_candies + + +def _distribute_candies_to_child(candies_per_child: int, child: Child): + # If a child eat more than 1 candies they're going to eat all + # the candies for sure + eaten_for_child = random.choices([0, 1, candies_per_child]) + print(f"Child {child} gets {candies_per_child} candies and eat {eaten_for_child}") + remaining_candies_for_children = child.eat_candies(eaten_for_child) + if remaining_candies_for_children == 0: + print(f"All the candies have been devoured by {child.name}!") + else: + print(f"{child.name} still have {remaining_candies_for_children} candies left.") + return eaten_for_child diff --git a/lib/common/pylint_data/messages/too-many-try-statements/bad.py b/lib/common/pylint_data/messages/too-many-try-statements/bad.py new file mode 100644 index 0000000..4e816ad --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-try-statements/bad.py @@ -0,0 +1,10 @@ +FRUITS = {"apple": 1, "orange": 10} + + +def pick_fruit(name): + try: # [too-many-try-statements] + count = FRUITS[name] + count += 1 + print(f"Got fruit count {count}") + except KeyError: + return diff --git a/lib/common/pylint_data/messages/too-many-try-statements/good.py b/lib/common/pylint_data/messages/too-many-try-statements/good.py new file mode 100644 index 0000000..faea966 --- /dev/null +++ b/lib/common/pylint_data/messages/too-many-try-statements/good.py @@ -0,0 +1,11 @@ +FRUITS = {"apple": 1, "orange": 10} + + +def pick_fruit(name): + try: + count = FRUITS[name] + except KeyError: + return + + count += 1 + print(f"Got fruit count {count}") diff --git a/lib/common/pylint_data/messages/trailing-comma-tuple/bad.py b/lib/common/pylint_data/messages/trailing-comma-tuple/bad.py new file mode 100644 index 0000000..8e8cad8 --- /dev/null +++ b/lib/common/pylint_data/messages/trailing-comma-tuple/bad.py @@ -0,0 +1 @@ +COMPASS = "north", "south", "east", "west", # [trailing-comma-tuple] diff --git a/lib/common/pylint_data/messages/trailing-comma-tuple/good.py b/lib/common/pylint_data/messages/trailing-comma-tuple/good.py new file mode 100644 index 0000000..d21eeba --- /dev/null +++ b/lib/common/pylint_data/messages/trailing-comma-tuple/good.py @@ -0,0 +1 @@ +COMPASS = ("north", "south", "east", "west") diff --git a/lib/common/pylint_data/messages/trailing-newlines/bad.py b/lib/common/pylint_data/messages/trailing-newlines/bad.py new file mode 100644 index 0000000..91f8090 --- /dev/null +++ b/lib/common/pylint_data/messages/trailing-newlines/bad.py @@ -0,0 +1,3 @@ +print("apple") +# The file ends with 2 lines that are empty # +1: [trailing-newlines] + diff --git a/lib/common/pylint_data/messages/trailing-newlines/good.py b/lib/common/pylint_data/messages/trailing-newlines/good.py new file mode 100644 index 0000000..5772579 --- /dev/null +++ b/lib/common/pylint_data/messages/trailing-newlines/good.py @@ -0,0 +1 @@ +print("apple") diff --git a/lib/common/pylint_data/messages/trailing-whitespace/bad.py b/lib/common/pylint_data/messages/trailing-whitespace/bad.py new file mode 100644 index 0000000..1b2cbf4 --- /dev/null +++ b/lib/common/pylint_data/messages/trailing-whitespace/bad.py @@ -0,0 +1,2 @@ +print("Hello") # [trailing-whitespace] +# ^^^ trailing whitespaces diff --git a/lib/common/pylint_data/messages/trailing-whitespace/good.py b/lib/common/pylint_data/messages/trailing-whitespace/good.py new file mode 100644 index 0000000..2f9a147 --- /dev/null +++ b/lib/common/pylint_data/messages/trailing-whitespace/good.py @@ -0,0 +1 @@ +print("Hello") diff --git a/lib/common/pylint_data/messages/truncated-format-string/bad.py b/lib/common/pylint_data/messages/truncated-format-string/bad.py new file mode 100644 index 0000000..a44bca2 --- /dev/null +++ b/lib/common/pylint_data/messages/truncated-format-string/bad.py @@ -0,0 +1,3 @@ +PARG_2 = 1 + +print("strange format %2" % PARG_2) # [truncated-format-string] diff --git a/lib/common/pylint_data/messages/truncated-format-string/good.py b/lib/common/pylint_data/messages/truncated-format-string/good.py new file mode 100644 index 0000000..fd65df9 --- /dev/null +++ b/lib/common/pylint_data/messages/truncated-format-string/good.py @@ -0,0 +1,3 @@ +PARG_2 = 1 + +print(f"strange format {PARG_2}") diff --git a/lib/common/pylint_data/messages/try-except-raise/bad.py b/lib/common/pylint_data/messages/try-except-raise/bad.py new file mode 100644 index 0000000..8db8da4 --- /dev/null +++ b/lib/common/pylint_data/messages/try-except-raise/bad.py @@ -0,0 +1,4 @@ +try: + 1 / 0 +except ZeroDivisionError as e: # [try-except-raise] + raise diff --git a/lib/common/pylint_data/messages/try-except-raise/details.md b/lib/common/pylint_data/messages/try-except-raise/details.md new file mode 100644 index 0000000..7291e9e --- /dev/null +++ b/lib/common/pylint_data/messages/try-except-raise/details.md @@ -0,0 +1,20 @@ +There is a legitimate use case for re-raising immediately. E.g. with the +following inheritance tree: + + +-- ArithmeticError + +-- FloatingPointError + +-- OverflowError + +-- ZeroDivisionError + +The following code shows valid case for re-raising exception +immediately: + + def execute_calculation(a, b): + try: + return some_calculation(a, b) + except ZeroDivisionError: + raise + except ArithmeticError: + return float('nan') + +The pylint is able to detect this case and does not produce error. diff --git a/lib/common/pylint_data/messages/try-except-raise/good.py b/lib/common/pylint_data/messages/try-except-raise/good.py new file mode 100644 index 0000000..40a3a1b --- /dev/null +++ b/lib/common/pylint_data/messages/try-except-raise/good.py @@ -0,0 +1,9 @@ +# The try except might be removed entirely: +1 / 0 + + +# Another more detailed exception can be raised: +try: + 1 / 0 +except ZeroDivisionError as e: + raise ValueError("The area of the rectangle cannot be zero") from e diff --git a/lib/common/pylint_data/messages/typevar-double-variance/bad.py b/lib/common/pylint_data/messages/typevar-double-variance/bad.py new file mode 100644 index 0000000..f24d1d7 --- /dev/null +++ b/lib/common/pylint_data/messages/typevar-double-variance/bad.py @@ -0,0 +1,3 @@ +from typing import TypeVar + +T = TypeVar("T", covariant=True, contravariant=True) # [typevar-double-variance] diff --git a/lib/common/pylint_data/messages/typevar-double-variance/good.py b/lib/common/pylint_data/messages/typevar-double-variance/good.py new file mode 100644 index 0000000..d7ae532 --- /dev/null +++ b/lib/common/pylint_data/messages/typevar-double-variance/good.py @@ -0,0 +1,4 @@ +from typing import TypeVar + +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) diff --git a/lib/common/pylint_data/messages/typevar-name-incorrect-variance/bad.py b/lib/common/pylint_data/messages/typevar-name-incorrect-variance/bad.py new file mode 100644 index 0000000..7cd634e --- /dev/null +++ b/lib/common/pylint_data/messages/typevar-name-incorrect-variance/bad.py @@ -0,0 +1,3 @@ +from typing import TypeVar + +T_co = TypeVar("T_co") # [typevar-name-incorrect-variance] diff --git a/lib/common/pylint_data/messages/typevar-name-incorrect-variance/details.md b/lib/common/pylint_data/messages/typevar-name-incorrect-variance/details.md new file mode 100644 index 0000000..f924a55 --- /dev/null +++ b/lib/common/pylint_data/messages/typevar-name-incorrect-variance/details.md @@ -0,0 +1,2 @@ +When naming type vars, only use a `_co` suffix when indicating +covariance or `_contra` when indicating contravariance. diff --git a/lib/common/pylint_data/messages/typevar-name-incorrect-variance/good.py b/lib/common/pylint_data/messages/typevar-name-incorrect-variance/good.py new file mode 100644 index 0000000..b186d5a --- /dev/null +++ b/lib/common/pylint_data/messages/typevar-name-incorrect-variance/good.py @@ -0,0 +1,3 @@ +from typing import TypeVar + +T = TypeVar("T") diff --git a/lib/common/pylint_data/messages/typevar-name-mismatch/bad.py b/lib/common/pylint_data/messages/typevar-name-mismatch/bad.py new file mode 100644 index 0000000..bdc814e --- /dev/null +++ b/lib/common/pylint_data/messages/typevar-name-mismatch/bad.py @@ -0,0 +1,3 @@ +from typing import TypeVar + +X = TypeVar("T") # [typevar-name-mismatch] diff --git a/lib/common/pylint_data/messages/typevar-name-mismatch/good.py b/lib/common/pylint_data/messages/typevar-name-mismatch/good.py new file mode 100644 index 0000000..b186d5a --- /dev/null +++ b/lib/common/pylint_data/messages/typevar-name-mismatch/good.py @@ -0,0 +1,3 @@ +from typing import TypeVar + +T = TypeVar("T") diff --git a/lib/common/pylint_data/messages/unbalanced-dict-unpacking/bad.py b/lib/common/pylint_data/messages/unbalanced-dict-unpacking/bad.py new file mode 100644 index 0000000..9162ccc --- /dev/null +++ b/lib/common/pylint_data/messages/unbalanced-dict-unpacking/bad.py @@ -0,0 +1,4 @@ +FRUITS = {"apple": 2, "orange": 3, "mellon": 10} + +for fruit, price in FRUITS.values(): # [unbalanced-dict-unpacking] + print(fruit) diff --git a/lib/common/pylint_data/messages/unbalanced-dict-unpacking/good.py b/lib/common/pylint_data/messages/unbalanced-dict-unpacking/good.py new file mode 100644 index 0000000..450e034 --- /dev/null +++ b/lib/common/pylint_data/messages/unbalanced-dict-unpacking/good.py @@ -0,0 +1,4 @@ +FRUITS = {"apple": 2, "orange": 3, "mellon": 10} + +for fruit, price in FRUITS.items(): + print(fruit) diff --git a/lib/common/pylint_data/messages/unbalanced-tuple-unpacking/bad.py b/lib/common/pylint_data/messages/unbalanced-tuple-unpacking/bad.py new file mode 100644 index 0000000..b927952 --- /dev/null +++ b/lib/common/pylint_data/messages/unbalanced-tuple-unpacking/bad.py @@ -0,0 +1,2 @@ +fruits = ("orange", "apple", "strawberry", "peer") +orange, apple, strawberry = fruits # [unbalanced-tuple-unpacking] diff --git a/lib/common/pylint_data/messages/unbalanced-tuple-unpacking/good.py b/lib/common/pylint_data/messages/unbalanced-tuple-unpacking/good.py new file mode 100644 index 0000000..8f49933 --- /dev/null +++ b/lib/common/pylint_data/messages/unbalanced-tuple-unpacking/good.py @@ -0,0 +1,2 @@ +fruits = ("orange", "apple", "strawberry", "peer") +orange, apple, *remaining_fruits = fruits diff --git a/lib/common/pylint_data/messages/unbalanced-tuple-unpacking/related.md b/lib/common/pylint_data/messages/unbalanced-tuple-unpacking/related.md new file mode 100644 index 0000000..bdee76b --- /dev/null +++ b/lib/common/pylint_data/messages/unbalanced-tuple-unpacking/related.md @@ -0,0 +1,2 @@ +- [PEP 3132 - Extended Iterable + Unpacking](https://peps.python.org/pep-3132/) diff --git a/lib/common/pylint_data/messages/undefined-all-variable/bad.py b/lib/common/pylint_data/messages/undefined-all-variable/bad.py new file mode 100644 index 0000000..3168018 --- /dev/null +++ b/lib/common/pylint_data/messages/undefined-all-variable/bad.py @@ -0,0 +1,5 @@ +__all__ = ["get_fruit_colour"] # [undefined-all-variable] + + +def get_fruit_color(): + pass diff --git a/lib/common/pylint_data/messages/undefined-all-variable/good.py b/lib/common/pylint_data/messages/undefined-all-variable/good.py new file mode 100644 index 0000000..950222b --- /dev/null +++ b/lib/common/pylint_data/messages/undefined-all-variable/good.py @@ -0,0 +1,5 @@ +__all__ = ["get_fruit_color"] + + +def get_fruit_color(): + pass diff --git a/lib/common/pylint_data/messages/undefined-all-variable/related.md b/lib/common/pylint_data/messages/undefined-all-variable/related.md new file mode 100644 index 0000000..03294c7 --- /dev/null +++ b/lib/common/pylint_data/messages/undefined-all-variable/related.md @@ -0,0 +1,2 @@ +- [Importing \* From a + Package](https://docs.python.org/3/tutorial/modules.html#importing-from-a-package) diff --git a/lib/common/pylint_data/messages/undefined-loop-variable/bad.py b/lib/common/pylint_data/messages/undefined-loop-variable/bad.py new file mode 100644 index 0000000..88b0c11 --- /dev/null +++ b/lib/common/pylint_data/messages/undefined-loop-variable/bad.py @@ -0,0 +1,5 @@ +def find_even_number(numbers): + for x in numbers: + if x % 2 == 0: + break + return x # [undefined-loop-variable] diff --git a/lib/common/pylint_data/messages/undefined-loop-variable/good.py b/lib/common/pylint_data/messages/undefined-loop-variable/good.py new file mode 100644 index 0000000..eaea172 --- /dev/null +++ b/lib/common/pylint_data/messages/undefined-loop-variable/good.py @@ -0,0 +1,5 @@ +def find_even_number(numbers): + for x in numbers: + if x % 2: + return x + return None diff --git a/lib/common/pylint_data/messages/undefined-variable/bad.py b/lib/common/pylint_data/messages/undefined-variable/bad.py new file mode 100644 index 0000000..27664df --- /dev/null +++ b/lib/common/pylint_data/messages/undefined-variable/bad.py @@ -0,0 +1 @@ +print(number + 2) # [undefined-variable] diff --git a/lib/common/pylint_data/messages/undefined-variable/good.py b/lib/common/pylint_data/messages/undefined-variable/good.py new file mode 100644 index 0000000..99c9282 --- /dev/null +++ b/lib/common/pylint_data/messages/undefined-variable/good.py @@ -0,0 +1,2 @@ +number = 3 +print(number + 2) diff --git a/lib/common/pylint_data/messages/unexpected-keyword-arg/bad.py b/lib/common/pylint_data/messages/unexpected-keyword-arg/bad.py new file mode 100644 index 0000000..40ef297 --- /dev/null +++ b/lib/common/pylint_data/messages/unexpected-keyword-arg/bad.py @@ -0,0 +1,5 @@ +def print_coordinates(x=0, y=0): + print(f"{x=}, {y=}") + + +print_coordinates(x=1, y=2, z=3) # [unexpected-keyword-arg] diff --git a/lib/common/pylint_data/messages/unexpected-keyword-arg/good.py b/lib/common/pylint_data/messages/unexpected-keyword-arg/good.py new file mode 100644 index 0000000..076b3f0 --- /dev/null +++ b/lib/common/pylint_data/messages/unexpected-keyword-arg/good.py @@ -0,0 +1,5 @@ +def print_coordinates(x=0, y=0): + print(f"{x=}, {y=}") + + +print_coordinates(x=1, y=2) diff --git a/lib/common/pylint_data/messages/unexpected-line-ending-format/bad.py b/lib/common/pylint_data/messages/unexpected-line-ending-format/bad.py new file mode 100644 index 0000000..2d94b08 --- /dev/null +++ b/lib/common/pylint_data/messages/unexpected-line-ending-format/bad.py @@ -0,0 +1,2 @@ +print("I'm drinking tea!") # CRLF (\r\n) # [unexpected-line-ending-format] +print("I'm drinking water!") # CRLF (\r\n) # [unexpected-line-ending-format] diff --git a/lib/common/pylint_data/messages/unexpected-line-ending-format/good.py b/lib/common/pylint_data/messages/unexpected-line-ending-format/good.py new file mode 100644 index 0000000..68b26fe --- /dev/null +++ b/lib/common/pylint_data/messages/unexpected-line-ending-format/good.py @@ -0,0 +1,2 @@ +print("I'm drinking tea!") # LF (\n) +print("I'm drinking water!") # LF (\n) diff --git a/lib/common/pylint_data/messages/unexpected-line-ending-format/related.md b/lib/common/pylint_data/messages/unexpected-line-ending-format/related.md new file mode 100644 index 0000000..89faf54 --- /dev/null +++ b/lib/common/pylint_data/messages/unexpected-line-ending-format/related.md @@ -0,0 +1,5 @@ +- [History of CRLF and LF](https://stackoverflow.com/a/6521730/2519059) +- [Dealing with line endings in + Git](https://stackoverflow.com/a/10855862/2519059) +- [A Collection of Useful .gitattributes + Templates](https://github.com/alexkaratarakis/gitattributes) diff --git a/lib/common/pylint_data/messages/unexpected-special-method-signature/bad.py b/lib/common/pylint_data/messages/unexpected-special-method-signature/bad.py new file mode 100644 index 0000000..4324d39 --- /dev/null +++ b/lib/common/pylint_data/messages/unexpected-special-method-signature/bad.py @@ -0,0 +1,6 @@ +class ContextManager: + def __enter__(self, context): # [unexpected-special-method-signature] + pass + + def __exit__(self, type): # [unexpected-special-method-signature] + pass diff --git a/lib/common/pylint_data/messages/unexpected-special-method-signature/good.py b/lib/common/pylint_data/messages/unexpected-special-method-signature/good.py new file mode 100644 index 0000000..89ef3b6 --- /dev/null +++ b/lib/common/pylint_data/messages/unexpected-special-method-signature/good.py @@ -0,0 +1,6 @@ +class ContextManager: + def __enter__(self): + pass + + def __exit__(self, type, value, traceback): + pass diff --git a/lib/common/pylint_data/messages/ungrouped-imports/bad.py b/lib/common/pylint_data/messages/ungrouped-imports/bad.py new file mode 100644 index 0000000..bb5f59f --- /dev/null +++ b/lib/common/pylint_data/messages/ungrouped-imports/bad.py @@ -0,0 +1,5 @@ +import logging +import os +import sys +import logging.config # [ungrouped-imports] +from logging.handlers import WatchedFileHandler diff --git a/lib/common/pylint_data/messages/ungrouped-imports/good.py b/lib/common/pylint_data/messages/ungrouped-imports/good.py new file mode 100644 index 0000000..79f9fb9 --- /dev/null +++ b/lib/common/pylint_data/messages/ungrouped-imports/good.py @@ -0,0 +1,5 @@ +import logging +import logging.config +import os +import sys +from logging.handlers import FileHandler diff --git a/lib/common/pylint_data/messages/unhashable-member/bad.py b/lib/common/pylint_data/messages/unhashable-member/bad.py new file mode 100644 index 0000000..30c47da --- /dev/null +++ b/lib/common/pylint_data/messages/unhashable-member/bad.py @@ -0,0 +1,2 @@ +# Print the number of apples: +print({"apple": 42}[["apple"]]) # [unhashable-member] diff --git a/lib/common/pylint_data/messages/unhashable-member/good.py b/lib/common/pylint_data/messages/unhashable-member/good.py new file mode 100644 index 0000000..1678a60 --- /dev/null +++ b/lib/common/pylint_data/messages/unhashable-member/good.py @@ -0,0 +1,2 @@ +# Print the number of apples: +print({"apple": 42}["apple"]) diff --git a/lib/common/pylint_data/messages/unidiomatic-typecheck/bad.py b/lib/common/pylint_data/messages/unidiomatic-typecheck/bad.py new file mode 100644 index 0000000..e1e5610 --- /dev/null +++ b/lib/common/pylint_data/messages/unidiomatic-typecheck/bad.py @@ -0,0 +1,3 @@ +test_score = {"Biology": 95, "History": 80} +if type(test_score) is dict: # [unidiomatic-typecheck] + pass diff --git a/lib/common/pylint_data/messages/unidiomatic-typecheck/good.py b/lib/common/pylint_data/messages/unidiomatic-typecheck/good.py new file mode 100644 index 0000000..c50c6e4 --- /dev/null +++ b/lib/common/pylint_data/messages/unidiomatic-typecheck/good.py @@ -0,0 +1,3 @@ +test_score = {"Biology": 95, "History": 80} +if isinstance(test_score, dict): + pass diff --git a/lib/common/pylint_data/messages/unidiomatic-typecheck/related.md b/lib/common/pylint_data/messages/unidiomatic-typecheck/related.md new file mode 100644 index 0000000..e57fcf7 --- /dev/null +++ b/lib/common/pylint_data/messages/unidiomatic-typecheck/related.md @@ -0,0 +1,4 @@ +- [Builtin function + type()](https://docs.python.org/3/library/functions.html#type) +- [Builtin function + isinstance()](https://docs.python.org/3/library/functions.html#isinstance) diff --git a/lib/common/pylint_data/messages/unknown-option-value/bad.py b/lib/common/pylint_data/messages/unknown-option-value/bad.py new file mode 100644 index 0000000..a45c4ce --- /dev/null +++ b/lib/common/pylint_data/messages/unknown-option-value/bad.py @@ -0,0 +1 @@ +# pylint: disable=missnig-docstring # [unknown-option-value] diff --git a/lib/common/pylint_data/messages/unknown-option-value/good.py b/lib/common/pylint_data/messages/unknown-option-value/good.py new file mode 100644 index 0000000..96e495e --- /dev/null +++ b/lib/common/pylint_data/messages/unknown-option-value/good.py @@ -0,0 +1 @@ +# pylint: disable=missing-docstring diff --git a/lib/common/pylint_data/messages/unnecessary-comprehension/bad.py b/lib/common/pylint_data/messages/unnecessary-comprehension/bad.py new file mode 100644 index 0000000..85be49a --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-comprehension/bad.py @@ -0,0 +1,3 @@ +NUMBERS = [1, 1, 2, 2, 3, 3] + +UNIQUE_NUMBERS = {number for number in NUMBERS} # [unnecessary-comprehension] diff --git a/lib/common/pylint_data/messages/unnecessary-comprehension/good.py b/lib/common/pylint_data/messages/unnecessary-comprehension/good.py new file mode 100644 index 0000000..58f40dd --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-comprehension/good.py @@ -0,0 +1,3 @@ +NUMBERS = [1, 1, 2, 2, 3, 3] + +UNIQUE_NUMBERS = set(NUMBERS) diff --git a/lib/common/pylint_data/messages/unnecessary-default-type-args/bad.py b/lib/common/pylint_data/messages/unnecessary-default-type-args/bad.py new file mode 100644 index 0000000..e3d9779 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-default-type-args/bad.py @@ -0,0 +1,4 @@ +from collections.abc import AsyncGenerator, Generator + +a1: AsyncGenerator[int, None] # [unnecessary-default-type-args] +b1: Generator[int, None, None] # [unnecessary-default-type-args] diff --git a/lib/common/pylint_data/messages/unnecessary-default-type-args/details.md b/lib/common/pylint_data/messages/unnecessary-default-type-args/details.md new file mode 100644 index 0000000..7aa473b --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-default-type-args/details.md @@ -0,0 +1,8 @@ +At the moment, this check only works for `Generator` and +`AsyncGenerator`. + +Starting with Python 3.13, the `SendType` and `ReturnType` default to +`None`. As such it\'s no longer necessary to specify them. The +`collections.abc` variants don\'t validate the number of type arguments. +Therefore the defaults for these can be used in earlier versions as +well. diff --git a/lib/common/pylint_data/messages/unnecessary-default-type-args/good.py b/lib/common/pylint_data/messages/unnecessary-default-type-args/good.py new file mode 100644 index 0000000..e77c0ee --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-default-type-args/good.py @@ -0,0 +1,4 @@ +from collections.abc import AsyncGenerator, Generator + +a1: AsyncGenerator[int] +b1: Generator[int] diff --git a/lib/common/pylint_data/messages/unnecessary-default-type-args/related.md b/lib/common/pylint_data/messages/unnecessary-default-type-args/related.md new file mode 100644 index 0000000..ab92551 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-default-type-args/related.md @@ -0,0 +1,4 @@ +- [Python documentation for + AsyncGenerator](https://docs.python.org/3.13/library/typing.html#typing.AsyncGenerator) +- [Python documentation for + Generator](https://docs.python.org/3.13/library/typing.html#typing.Generator) diff --git a/lib/common/pylint_data/messages/unnecessary-dict-index-lookup/bad.py b/lib/common/pylint_data/messages/unnecessary-dict-index-lookup/bad.py new file mode 100644 index 0000000..0471758 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-dict-index-lookup/bad.py @@ -0,0 +1,4 @@ +FRUITS = {"apple": 1, "orange": 10, "berry": 22} + +for fruit_name, fruit_count in FRUITS.items(): + print(FRUITS[fruit_name]) # [unnecessary-dict-index-lookup] diff --git a/lib/common/pylint_data/messages/unnecessary-dict-index-lookup/good.py b/lib/common/pylint_data/messages/unnecessary-dict-index-lookup/good.py new file mode 100644 index 0000000..c2ee5ed --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-dict-index-lookup/good.py @@ -0,0 +1,4 @@ +FRUITS = {"apple": 1, "orange": 10, "berry": 22} + +for fruit_name, fruit_count in FRUITS.items(): + print(fruit_count) diff --git a/lib/common/pylint_data/messages/unnecessary-direct-lambda-call/bad.py b/lib/common/pylint_data/messages/unnecessary-direct-lambda-call/bad.py new file mode 100644 index 0000000..280fb3b --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-direct-lambda-call/bad.py @@ -0,0 +1 @@ +y = (lambda x: x**2 + 2 * x + 1)(a) # [unnecessary-direct-lambda-call] diff --git a/lib/common/pylint_data/messages/unnecessary-direct-lambda-call/good.py b/lib/common/pylint_data/messages/unnecessary-direct-lambda-call/good.py new file mode 100644 index 0000000..5870a4a --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-direct-lambda-call/good.py @@ -0,0 +1 @@ +y = a**2 + 2 * a + 1 diff --git a/lib/common/pylint_data/messages/unnecessary-dunder-call/bad.py b/lib/common/pylint_data/messages/unnecessary-dunder-call/bad.py new file mode 100644 index 0000000..49405d4 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-dunder-call/bad.py @@ -0,0 +1,6 @@ +three = (3.0).__str__() # [unnecessary-dunder-call] +twelve = "1".__add__("2") # [unnecessary-dunder-call] + + +def is_bigger_than_two(x): + return x.__gt__(2) # [unnecessary-dunder-call] diff --git a/lib/common/pylint_data/messages/unnecessary-dunder-call/good.py b/lib/common/pylint_data/messages/unnecessary-dunder-call/good.py new file mode 100644 index 0000000..fe41db7 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-dunder-call/good.py @@ -0,0 +1,6 @@ +three = str(3.0) +twelve = "1" + "2" + + +def is_bigger_than_two(x): + return x > 2 diff --git a/lib/common/pylint_data/messages/unnecessary-dunder-call/related.md b/lib/common/pylint_data/messages/unnecessary-dunder-call/related.md new file mode 100644 index 0000000..f3c9172 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-dunder-call/related.md @@ -0,0 +1,2 @@ +- [Define dunder methods but don\'t call them + directly](https://www.pythonmorsels.com/avoid-dunder-methods/) diff --git a/lib/common/pylint_data/messages/unnecessary-ellipsis/bad.py b/lib/common/pylint_data/messages/unnecessary-ellipsis/bad.py new file mode 100644 index 0000000..214be66 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-ellipsis/bad.py @@ -0,0 +1,3 @@ +def my_function(): + """My docstring""" + ... # [unnecessary-ellipsis] diff --git a/lib/common/pylint_data/messages/unnecessary-ellipsis/good.py b/lib/common/pylint_data/messages/unnecessary-ellipsis/good.py new file mode 100644 index 0000000..024b35a --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-ellipsis/good.py @@ -0,0 +1,2 @@ +def my_function(): + """My docstring""" diff --git a/lib/common/pylint_data/messages/unnecessary-lambda-assignment/bad.py b/lib/common/pylint_data/messages/unnecessary-lambda-assignment/bad.py new file mode 100644 index 0000000..87e7ccd --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-lambda-assignment/bad.py @@ -0,0 +1 @@ +foo = lambda x: x**2 + 2 * x + 1 # [unnecessary-lambda-assignment] diff --git a/lib/common/pylint_data/messages/unnecessary-lambda-assignment/good.py b/lib/common/pylint_data/messages/unnecessary-lambda-assignment/good.py new file mode 100644 index 0000000..5c7da57 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-lambda-assignment/good.py @@ -0,0 +1,2 @@ +def foo(x): + return x**2 + 2 * x + 1 diff --git a/lib/common/pylint_data/messages/unnecessary-lambda/bad.py b/lib/common/pylint_data/messages/unnecessary-lambda/bad.py new file mode 100644 index 0000000..c7e91c3 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-lambda/bad.py @@ -0,0 +1,5 @@ +function = lambda x: print(x) # [unnecessary-lambda] + +function("Hello world !") + +df.apply(lambda x: str(x)) # [unnecessary-lambda] \ No newline at end of file diff --git a/lib/common/pylint_data/messages/unnecessary-lambda/good.py b/lib/common/pylint_data/messages/unnecessary-lambda/good.py new file mode 100644 index 0000000..9f0678f --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-lambda/good.py @@ -0,0 +1,3 @@ +print("Hello world !") + +df.apply(str) diff --git a/lib/common/pylint_data/messages/unnecessary-list-index-lookup/bad.py b/lib/common/pylint_data/messages/unnecessary-list-index-lookup/bad.py new file mode 100644 index 0000000..b671b3a --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-list-index-lookup/bad.py @@ -0,0 +1,4 @@ +letters = ["a", "b", "c"] + +for index, letter in enumerate(letters): + print(letters[index]) # [unnecessary-list-index-lookup] diff --git a/lib/common/pylint_data/messages/unnecessary-list-index-lookup/good.py b/lib/common/pylint_data/messages/unnecessary-list-index-lookup/good.py new file mode 100644 index 0000000..2d1a342 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-list-index-lookup/good.py @@ -0,0 +1,4 @@ +letters = ["a", "b", "c"] + +for index, letter in enumerate(letters): + print(letter) diff --git a/lib/common/pylint_data/messages/unnecessary-negation/bad.py b/lib/common/pylint_data/messages/unnecessary-negation/bad.py new file mode 100644 index 0000000..377f1d7 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-negation/bad.py @@ -0,0 +1,8 @@ +if not not input(): # [unnecessary-negation] + pass + +# Example 2 +a = 3 +b = 10 +if not a > b: # [unnecessary-negation] + pass diff --git a/lib/common/pylint_data/messages/unnecessary-negation/good.py b/lib/common/pylint_data/messages/unnecessary-negation/good.py new file mode 100644 index 0000000..bb0b9c0 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-negation/good.py @@ -0,0 +1,9 @@ +# Example 1 +if input(): + pass + +# Example 2 +a = 3 +b = 10 +if a <= b: + pass diff --git a/lib/common/pylint_data/messages/unnecessary-pass/bad.py b/lib/common/pylint_data/messages/unnecessary-pass/bad.py new file mode 100644 index 0000000..700c9ae --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-pass/bad.py @@ -0,0 +1,4 @@ +class DataEntryError(Exception): + """This exception is raised when a user has provided incorrect data.""" + + pass # [unnecessary-pass] diff --git a/lib/common/pylint_data/messages/unnecessary-pass/good.py b/lib/common/pylint_data/messages/unnecessary-pass/good.py new file mode 100644 index 0000000..028191d --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-pass/good.py @@ -0,0 +1,2 @@ +class DataEntryError(Exception): + """This exception is raised when a user has provided incorrect data.""" diff --git a/lib/common/pylint_data/messages/unnecessary-semicolon/bad.py b/lib/common/pylint_data/messages/unnecessary-semicolon/bad.py new file mode 100644 index 0000000..3328e25 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-semicolon/bad.py @@ -0,0 +1 @@ +print("Hello World!"); # [unnecessary-semicolon] diff --git a/lib/common/pylint_data/messages/unnecessary-semicolon/good.py b/lib/common/pylint_data/messages/unnecessary-semicolon/good.py new file mode 100644 index 0000000..f301245 --- /dev/null +++ b/lib/common/pylint_data/messages/unnecessary-semicolon/good.py @@ -0,0 +1 @@ +print("Hello World!") diff --git a/lib/common/pylint_data/messages/unpacking-non-sequence/bad.py b/lib/common/pylint_data/messages/unpacking-non-sequence/bad.py new file mode 100644 index 0000000..ea02cfa --- /dev/null +++ b/lib/common/pylint_data/messages/unpacking-non-sequence/bad.py @@ -0,0 +1 @@ +a, b, c = 1 # [unpacking-non-sequence] diff --git a/lib/common/pylint_data/messages/unpacking-non-sequence/good.py b/lib/common/pylint_data/messages/unpacking-non-sequence/good.py new file mode 100644 index 0000000..1e006da --- /dev/null +++ b/lib/common/pylint_data/messages/unpacking-non-sequence/good.py @@ -0,0 +1 @@ +a, b, c = 1, 2, 3 diff --git a/lib/common/pylint_data/messages/unreachable/bad.py b/lib/common/pylint_data/messages/unreachable/bad.py new file mode 100644 index 0000000..343a118 --- /dev/null +++ b/lib/common/pylint_data/messages/unreachable/bad.py @@ -0,0 +1,3 @@ +def say_hello(): + return True + print("Hello World!, Outside function.") # [unreachable] diff --git a/lib/common/pylint_data/messages/unreachable/good.py b/lib/common/pylint_data/messages/unreachable/good.py new file mode 100644 index 0000000..e86879d --- /dev/null +++ b/lib/common/pylint_data/messages/unreachable/good.py @@ -0,0 +1,3 @@ +def say_hello(): + print("Hello World!, Inside function.") + return True diff --git a/lib/common/pylint_data/messages/unrecognized-inline-option/bad.py b/lib/common/pylint_data/messages/unrecognized-inline-option/bad.py new file mode 100644 index 0000000..ff49aa9 --- /dev/null +++ b/lib/common/pylint_data/messages/unrecognized-inline-option/bad.py @@ -0,0 +1,2 @@ +# +1: [unrecognized-inline-option] +# pylint:applesoranges=1 diff --git a/lib/common/pylint_data/messages/unrecognized-inline-option/good.py b/lib/common/pylint_data/messages/unrecognized-inline-option/good.py new file mode 100644 index 0000000..2fdb378 --- /dev/null +++ b/lib/common/pylint_data/messages/unrecognized-inline-option/good.py @@ -0,0 +1 @@ +# pylint: enable=too-many-public-methods diff --git a/lib/common/pylint_data/messages/unrecognized-option/details.md b/lib/common/pylint_data/messages/unrecognized-option/details.md new file mode 100644 index 0000000..1361620 --- /dev/null +++ b/lib/common/pylint_data/messages/unrecognized-option/details.md @@ -0,0 +1,18 @@ +One of your options is not recognized. There\'s nothing to change in +your code, but your pylint configuration or the way you launch pylint +needs to be modified. + +For example, this message would be raised when invoking pylint with +`pylint --unknown-option=yes test.py`. Or you might be launching pylint +with the following `toml` configuration: + + [tool.pylint] + jars = "10" + +When the following should be used: + + [tool.pylint] + jobs = "10" + +This warning was released in pylint 2.14: bad options were silently +failing before. diff --git a/lib/common/pylint_data/messages/unspecified-encoding/bad.py b/lib/common/pylint_data/messages/unspecified-encoding/bad.py new file mode 100644 index 0000000..4645923 --- /dev/null +++ b/lib/common/pylint_data/messages/unspecified-encoding/bad.py @@ -0,0 +1,3 @@ +def foo(file_path): + with open(file_path) as file: # [unspecified-encoding] + contents = file.read() diff --git a/lib/common/pylint_data/messages/unspecified-encoding/good.py b/lib/common/pylint_data/messages/unspecified-encoding/good.py new file mode 100644 index 0000000..a267a36 --- /dev/null +++ b/lib/common/pylint_data/messages/unspecified-encoding/good.py @@ -0,0 +1,3 @@ +def foo(file_path): + with open(file_path, encoding="utf-8") as file: + contents = file.read() diff --git a/lib/common/pylint_data/messages/unsubscriptable-object/bad.py b/lib/common/pylint_data/messages/unsubscriptable-object/bad.py new file mode 100644 index 0000000..8b168a0 --- /dev/null +++ b/lib/common/pylint_data/messages/unsubscriptable-object/bad.py @@ -0,0 +1,5 @@ +class Fruit: + pass + + +Fruit()[1] # [unsubscriptable-object] diff --git a/lib/common/pylint_data/messages/unsubscriptable-object/good.py b/lib/common/pylint_data/messages/unsubscriptable-object/good.py new file mode 100644 index 0000000..56e9144 --- /dev/null +++ b/lib/common/pylint_data/messages/unsubscriptable-object/good.py @@ -0,0 +1,9 @@ +class Fruit: + def __init__(self): + self.colors = ["red", "orange", "yellow"] + + def __getitem__(self, idx): + return self.colors[idx] + + +Fruit()[1] diff --git a/lib/common/pylint_data/messages/unsupported-assignment-operation/bad.py b/lib/common/pylint_data/messages/unsupported-assignment-operation/bad.py new file mode 100644 index 0000000..26ee1a9 --- /dev/null +++ b/lib/common/pylint_data/messages/unsupported-assignment-operation/bad.py @@ -0,0 +1,6 @@ +def pick_fruits(fruits): + for fruit in fruits: + print(fruit) + + +pick_fruits(["apple"])[0] = "orange" # [unsupported-assignment-operation] diff --git a/lib/common/pylint_data/messages/unsupported-assignment-operation/good.py b/lib/common/pylint_data/messages/unsupported-assignment-operation/good.py new file mode 100644 index 0000000..13fe34c --- /dev/null +++ b/lib/common/pylint_data/messages/unsupported-assignment-operation/good.py @@ -0,0 +1,8 @@ +def pick_fruits(fruits): + for fruit in fruits: + print(fruit) + + return [] + + +pick_fruits(["apple"])[0] = "orange" diff --git a/lib/common/pylint_data/messages/unsupported-binary-operation/bad.py b/lib/common/pylint_data/messages/unsupported-binary-operation/bad.py new file mode 100644 index 0000000..c816dde --- /dev/null +++ b/lib/common/pylint_data/messages/unsupported-binary-operation/bad.py @@ -0,0 +1,2 @@ +drink = "water" | None # [unsupported-binary-operation] +result = [] | None # [unsupported-binary-operation] diff --git a/lib/common/pylint_data/messages/unsupported-binary-operation/good.py b/lib/common/pylint_data/messages/unsupported-binary-operation/good.py new file mode 100644 index 0000000..6a6be9a --- /dev/null +++ b/lib/common/pylint_data/messages/unsupported-binary-operation/good.py @@ -0,0 +1,2 @@ +masked = 0b111111 & 0b001100 +result = 0xAEFF | 0x0B99 diff --git a/lib/common/pylint_data/messages/unsupported-delete-operation/bad.py b/lib/common/pylint_data/messages/unsupported-delete-operation/bad.py new file mode 100644 index 0000000..a7870e3 --- /dev/null +++ b/lib/common/pylint_data/messages/unsupported-delete-operation/bad.py @@ -0,0 +1,3 @@ +FRUITS = ("apple", "orange", "berry") + +del FRUITS[0] # [unsupported-delete-operation] diff --git a/lib/common/pylint_data/messages/unsupported-delete-operation/good.py b/lib/common/pylint_data/messages/unsupported-delete-operation/good.py new file mode 100644 index 0000000..8143c4f --- /dev/null +++ b/lib/common/pylint_data/messages/unsupported-delete-operation/good.py @@ -0,0 +1,3 @@ +FRUITS = ["apple", "orange", "berry"] + +del FRUITS[0] diff --git a/lib/common/pylint_data/messages/unsupported-membership-test/bad.py b/lib/common/pylint_data/messages/unsupported-membership-test/bad.py new file mode 100644 index 0000000..37502ec --- /dev/null +++ b/lib/common/pylint_data/messages/unsupported-membership-test/bad.py @@ -0,0 +1,5 @@ +class Fruit: + pass + + +apple = "apple" in Fruit() # [unsupported-membership-test] diff --git a/lib/common/pylint_data/messages/unsupported-membership-test/good.py b/lib/common/pylint_data/messages/unsupported-membership-test/good.py new file mode 100644 index 0000000..a5af58d --- /dev/null +++ b/lib/common/pylint_data/messages/unsupported-membership-test/good.py @@ -0,0 +1,8 @@ +class Fruit: + FRUITS = ["apple", "orange"] + + def __contains__(self, name): + return name in self.FRUITS + + +apple = "apple" in Fruit() diff --git a/lib/common/pylint_data/messages/unused-argument/bad.py b/lib/common/pylint_data/messages/unused-argument/bad.py new file mode 100644 index 0000000..c605fb7 --- /dev/null +++ b/lib/common/pylint_data/messages/unused-argument/bad.py @@ -0,0 +1,2 @@ +def print_point(x, y): # [unused-argument] + print(f"Point is located at {x},{x}") diff --git a/lib/common/pylint_data/messages/unused-argument/good.py b/lib/common/pylint_data/messages/unused-argument/good.py new file mode 100644 index 0000000..a093d7d --- /dev/null +++ b/lib/common/pylint_data/messages/unused-argument/good.py @@ -0,0 +1,2 @@ +def print_point(x, y): + print(f"Point is located at {x},{y}") diff --git a/lib/common/pylint_data/messages/unused-format-string-argument/bad.py b/lib/common/pylint_data/messages/unused-format-string-argument/bad.py new file mode 100644 index 0000000..49eda97 --- /dev/null +++ b/lib/common/pylint_data/messages/unused-format-string-argument/bad.py @@ -0,0 +1 @@ +print("{x} {y}".format(x=1, y=2, z=3)) # [unused-format-string-argument] diff --git a/lib/common/pylint_data/messages/unused-format-string-argument/good.py b/lib/common/pylint_data/messages/unused-format-string-argument/good.py new file mode 100644 index 0000000..bf57811 --- /dev/null +++ b/lib/common/pylint_data/messages/unused-format-string-argument/good.py @@ -0,0 +1,5 @@ +# Add a format target +print("{x} {y} {z}".format(x=1, y=2, z=3)) + +# Remove unused args +print("{x} {y}".format(x=1, y=2)) diff --git a/lib/common/pylint_data/messages/unused-format-string-key/bad.py b/lib/common/pylint_data/messages/unused-format-string-key/bad.py new file mode 100644 index 0000000..7a326da --- /dev/null +++ b/lib/common/pylint_data/messages/unused-format-string-key/bad.py @@ -0,0 +1,5 @@ +"The quick %(color)s fox jumps over the lazy dog." % { + "color": "brown", + "action": "hops", +} +# -4: [unused-format-string-key] diff --git a/lib/common/pylint_data/messages/unused-format-string-key/good.py b/lib/common/pylint_data/messages/unused-format-string-key/good.py new file mode 100644 index 0000000..9347a3d --- /dev/null +++ b/lib/common/pylint_data/messages/unused-format-string-key/good.py @@ -0,0 +1,4 @@ +"The quick %(color)s fox %(action)s over the lazy dog." % { + "color": "brown", + "action": "hops", +} diff --git a/lib/common/pylint_data/messages/unused-import/bad.py b/lib/common/pylint_data/messages/unused-import/bad.py new file mode 100644 index 0000000..79bb4e7 --- /dev/null +++ b/lib/common/pylint_data/messages/unused-import/bad.py @@ -0,0 +1,4 @@ +from logging import getLogger +from pathlib import Path # [unused-import] + +LOGGER = getLogger(__name__) diff --git a/lib/common/pylint_data/messages/unused-import/details.md b/lib/common/pylint_data/messages/unused-import/details.md new file mode 100644 index 0000000..66b7500 --- /dev/null +++ b/lib/common/pylint_data/messages/unused-import/details.md @@ -0,0 +1,5 @@ +By default, this check is skipped for `__init__.py` files, as they often +contain imports from submodules for the convenience of end users. While +these imports are not used within `__init__.py`, they serve the purpose +of providing intuitive import paths for the module\'s important classes +and constants. diff --git a/lib/common/pylint_data/messages/unused-import/good.py b/lib/common/pylint_data/messages/unused-import/good.py new file mode 100644 index 0000000..81917fe --- /dev/null +++ b/lib/common/pylint_data/messages/unused-import/good.py @@ -0,0 +1,3 @@ +from logging import getLogger + +LOGGER = getLogger(__name__) diff --git a/lib/common/pylint_data/messages/unused-import/related.md b/lib/common/pylint_data/messages/unused-import/related.md new file mode 100644 index 0000000..5673388 --- /dev/null +++ b/lib/common/pylint_data/messages/unused-import/related.md @@ -0,0 +1 @@ +- `--init-import `{.interpreted-text role="ref"} diff --git a/lib/common/pylint_data/messages/unused-private-member/bad.py b/lib/common/pylint_data/messages/unused-private-member/bad.py new file mode 100644 index 0000000..b56bcaa --- /dev/null +++ b/lib/common/pylint_data/messages/unused-private-member/bad.py @@ -0,0 +1,5 @@ +class Fruit: + FRUITS = {"apple": "red", "orange": "orange"} + + def __print_color(self): # [unused-private-member] + pass diff --git a/lib/common/pylint_data/messages/unused-private-member/good.py b/lib/common/pylint_data/messages/unused-private-member/good.py new file mode 100644 index 0000000..02df36c --- /dev/null +++ b/lib/common/pylint_data/messages/unused-private-member/good.py @@ -0,0 +1,9 @@ +class Fruit: + FRUITS = {"apple": "red", "orange": "orange"} + + def __print_color(self, name, color): + print(f"{name}: {color}") + + def print(self): + for fruit, color in self.FRUITS.items(): + self.__print_color(fruit, color) diff --git a/lib/common/pylint_data/messages/unused-variable/bad.py b/lib/common/pylint_data/messages/unused-variable/bad.py new file mode 100644 index 0000000..1de7d2b --- /dev/null +++ b/lib/common/pylint_data/messages/unused-variable/bad.py @@ -0,0 +1,4 @@ +def print_fruits(): + fruit1 = "orange" + fruit2 = "apple" # [unused-variable] + print(fruit1) diff --git a/lib/common/pylint_data/messages/unused-variable/good.py b/lib/common/pylint_data/messages/unused-variable/good.py new file mode 100644 index 0000000..69b7663 --- /dev/null +++ b/lib/common/pylint_data/messages/unused-variable/good.py @@ -0,0 +1,4 @@ +def print_fruits(): + fruit1 = "orange" + fruit2 = "apple" + print(fruit1, fruit2) diff --git a/lib/common/pylint_data/messages/unused-wildcard-import/bad.py b/lib/common/pylint_data/messages/unused-wildcard-import/bad.py new file mode 100644 index 0000000..8d48581 --- /dev/null +++ b/lib/common/pylint_data/messages/unused-wildcard-import/bad.py @@ -0,0 +1,4 @@ +from abc import * # [unused-wildcard-import] + + +class Animal(ABC): ... diff --git a/lib/common/pylint_data/messages/unused-wildcard-import/detail.md b/lib/common/pylint_data/messages/unused-wildcard-import/detail.md new file mode 100644 index 0000000..b12de09 --- /dev/null +++ b/lib/common/pylint_data/messages/unused-wildcard-import/detail.md @@ -0,0 +1,2 @@ +Either remove the wildcard import, make use of every object from the +wildcard import, or only import the required objects. diff --git a/lib/common/pylint_data/messages/unused-wildcard-import/good.py b/lib/common/pylint_data/messages/unused-wildcard-import/good.py new file mode 100644 index 0000000..961af99 --- /dev/null +++ b/lib/common/pylint_data/messages/unused-wildcard-import/good.py @@ -0,0 +1,4 @@ +from abc import ABC + + +class Animal(ABC): ... diff --git a/lib/common/pylint_data/messages/use-a-generator/bad.py b/lib/common/pylint_data/messages/use-a-generator/bad.py new file mode 100644 index 0000000..bc68ede --- /dev/null +++ b/lib/common/pylint_data/messages/use-a-generator/bad.py @@ -0,0 +1,4 @@ +from random import randint + +all([randint(-5, 5) > 0 for _ in range(10)]) # [use-a-generator] +any([randint(-5, 5) > 0 for _ in range(10)]) # [use-a-generator] diff --git a/lib/common/pylint_data/messages/use-a-generator/details.md b/lib/common/pylint_data/messages/use-a-generator/details.md new file mode 100644 index 0000000..2f59958 --- /dev/null +++ b/lib/common/pylint_data/messages/use-a-generator/details.md @@ -0,0 +1,5 @@ +By using a generator you can cut the execution tree and exit directly at +the first element that is `False` for `all` or `True` for `any` instead +of calculating all the elements. Except in the worst possible case where +you still need to evaluate everything (all values are True for `all` or +all values are false for `any`) performance will be better. diff --git a/lib/common/pylint_data/messages/use-a-generator/good.py b/lib/common/pylint_data/messages/use-a-generator/good.py new file mode 100644 index 0000000..c5cda85 --- /dev/null +++ b/lib/common/pylint_data/messages/use-a-generator/good.py @@ -0,0 +1,4 @@ +from random import randint + +all(randint(-5, 5) > 0 for _ in range(10)) +any(randint(-5, 5) > 0 for _ in range(10)) diff --git a/lib/common/pylint_data/messages/use-a-generator/related.md b/lib/common/pylint_data/messages/use-a-generator/related.md new file mode 100644 index 0000000..dbc2c67 --- /dev/null +++ b/lib/common/pylint_data/messages/use-a-generator/related.md @@ -0,0 +1,3 @@ +- [PEP 289 -- Generator Expressions](https://peps.python.org/pep-0289/) +- [Benchmark and discussion during initial + implementation](https://github.com/pylint-dev/pylint/pull/3309#discussion_r576683109) diff --git a/lib/common/pylint_data/messages/use-dict-literal/bad.py b/lib/common/pylint_data/messages/use-dict-literal/bad.py new file mode 100644 index 0000000..1701621 --- /dev/null +++ b/lib/common/pylint_data/messages/use-dict-literal/bad.py @@ -0,0 +1,7 @@ +empty_dict = dict() # [use-dict-literal] + +original_dict = {"name": "Sunny", "age": 10, "favorite_color": "yellow"} +copied_dict = dict(**original_dict) # [use-dict-literal] + +response_dict = dict(answer="No") # [use-dict-literal] + diff --git a/lib/common/pylint_data/messages/use-dict-literal/details.md b/lib/common/pylint_data/messages/use-dict-literal/details.md new file mode 100644 index 0000000..3dfc8da --- /dev/null +++ b/lib/common/pylint_data/messages/use-dict-literal/details.md @@ -0,0 +1,4 @@ + + +This example script shows an 18% increase in performance when using a +literal over the constructor in python version 3.10.6. diff --git a/lib/common/pylint_data/messages/use-dict-literal/good.py b/lib/common/pylint_data/messages/use-dict-literal/good.py new file mode 100644 index 0000000..50ca531 --- /dev/null +++ b/lib/common/pylint_data/messages/use-dict-literal/good.py @@ -0,0 +1,7 @@ +empty_dict = {} + +original_dict = {"name": "Sunny", "age": 10, "favorite_color": "yellow"} +# shallow copy a dict +copied_dict = {**original_dict} + +response_dict = {"answer": "No"} diff --git a/lib/common/pylint_data/messages/use-dict-literal/related.md b/lib/common/pylint_data/messages/use-dict-literal/related.md new file mode 100644 index 0000000..87359b7 --- /dev/null +++ b/lib/common/pylint_data/messages/use-dict-literal/related.md @@ -0,0 +1,2 @@ +- [Performance Analysis of Python's dict() vs dict + literal](https://madebyme.today/blog/python-dict-vs-curly-brackets/) diff --git a/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-string/bad.py b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-string/bad.py new file mode 100644 index 0000000..119fd8b --- /dev/null +++ b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-string/bad.py @@ -0,0 +1,6 @@ +def important_string_manipulation(x: str, y: str) -> None: + if x == "": # [use-implicit-booleaness-not-comparison-to-string] + print("x is an empty string") + + if y != "": # [use-implicit-booleaness-not-comparison-to-string] + print("y is not an empty string") diff --git a/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-string/good.py b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-string/good.py new file mode 100644 index 0000000..21f222e --- /dev/null +++ b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-string/good.py @@ -0,0 +1,6 @@ +def important_string_manipulation(x: str, y: str) -> None: + if not x: + print("x is an empty string") + + if y: + print("y is not an empty string") diff --git a/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-zero/bad.py b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-zero/bad.py new file mode 100644 index 0000000..2f93afd --- /dev/null +++ b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-zero/bad.py @@ -0,0 +1,6 @@ +def important_math(x: int, y: int) -> None: + if x == 0: # [use-implicit-booleaness-not-comparison-to-zero] + print("x is equal to zero") + + if y != 0: # [use-implicit-booleaness-not-comparison-to-zero] + print("y is not equal to zero") diff --git a/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-zero/good.py b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-zero/good.py new file mode 100644 index 0000000..feae7fe --- /dev/null +++ b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison-to-zero/good.py @@ -0,0 +1,6 @@ +def important_math(x: int, y: int) -> None: + if not x: + print("x is equal to zero") + + if y: + print("y is not equal to zero") diff --git a/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison/bad.py b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison/bad.py new file mode 100644 index 0000000..78411ec --- /dev/null +++ b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison/bad.py @@ -0,0 +1,4 @@ +z = [] + +if z != []: # [use-implicit-booleaness-not-comparison] + print("z is not an empty sequence") diff --git a/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison/good.py b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison/good.py new file mode 100644 index 0000000..6801d91 --- /dev/null +++ b/lib/common/pylint_data/messages/use-implicit-booleaness-not-comparison/good.py @@ -0,0 +1,4 @@ +z = [] + +if z: + print("z is not an empty sequence") diff --git a/lib/common/pylint_data/messages/use-implicit-booleaness-not-len/bad.py b/lib/common/pylint_data/messages/use-implicit-booleaness-not-len/bad.py new file mode 100644 index 0000000..722282a --- /dev/null +++ b/lib/common/pylint_data/messages/use-implicit-booleaness-not-len/bad.py @@ -0,0 +1,4 @@ +fruits = ["orange", "apple"] + +if len(fruits): # [use-implicit-booleaness-not-len] + print(fruits) diff --git a/lib/common/pylint_data/messages/use-implicit-booleaness-not-len/good.py b/lib/common/pylint_data/messages/use-implicit-booleaness-not-len/good.py new file mode 100644 index 0000000..0e84921 --- /dev/null +++ b/lib/common/pylint_data/messages/use-implicit-booleaness-not-len/good.py @@ -0,0 +1,4 @@ +fruits = ["orange", "apple"] + +if fruits: + print(fruits) diff --git a/lib/common/pylint_data/messages/use-list-literal/bad.py b/lib/common/pylint_data/messages/use-list-literal/bad.py new file mode 100644 index 0000000..801e029 --- /dev/null +++ b/lib/common/pylint_data/messages/use-list-literal/bad.py @@ -0,0 +1 @@ +empty_list = list() # [use-list-literal] diff --git a/lib/common/pylint_data/messages/use-list-literal/good.py b/lib/common/pylint_data/messages/use-list-literal/good.py new file mode 100644 index 0000000..d21ca61 --- /dev/null +++ b/lib/common/pylint_data/messages/use-list-literal/good.py @@ -0,0 +1 @@ +empty_list = [] diff --git a/lib/common/pylint_data/messages/use-maxsplit-arg/bad.py b/lib/common/pylint_data/messages/use-maxsplit-arg/bad.py new file mode 100644 index 0000000..8a0aa22 --- /dev/null +++ b/lib/common/pylint_data/messages/use-maxsplit-arg/bad.py @@ -0,0 +1,2 @@ +url = "www.example.com" +suffix = url.split(".")[-1] # [use-maxsplit-arg] diff --git a/lib/common/pylint_data/messages/use-maxsplit-arg/details.md b/lib/common/pylint_data/messages/use-maxsplit-arg/details.md new file mode 100644 index 0000000..f31978f --- /dev/null +++ b/lib/common/pylint_data/messages/use-maxsplit-arg/details.md @@ -0,0 +1,3 @@ +Be aware that the performance improvement from not splitting the string +so many times will only be realized in cases presenting more instances +of the splitting character than the minimal example here. diff --git a/lib/common/pylint_data/messages/use-maxsplit-arg/good.py b/lib/common/pylint_data/messages/use-maxsplit-arg/good.py new file mode 100644 index 0000000..39fe351 --- /dev/null +++ b/lib/common/pylint_data/messages/use-maxsplit-arg/good.py @@ -0,0 +1,2 @@ +url = "www.example.com" +suffix = url.rsplit(".", maxsplit=1)[-1] diff --git a/lib/common/pylint_data/messages/use-sequence-for-iteration/bad.py b/lib/common/pylint_data/messages/use-sequence-for-iteration/bad.py new file mode 100644 index 0000000..8a48e6b --- /dev/null +++ b/lib/common/pylint_data/messages/use-sequence-for-iteration/bad.py @@ -0,0 +1,2 @@ +for food in {"apples", "lemons", "water"}: # [use-sequence-for-iteration] + print(f"I like {food}.") diff --git a/lib/common/pylint_data/messages/use-sequence-for-iteration/details.md b/lib/common/pylint_data/messages/use-sequence-for-iteration/details.md new file mode 100644 index 0000000..8e89ac8 --- /dev/null +++ b/lib/common/pylint_data/messages/use-sequence-for-iteration/details.md @@ -0,0 +1,4 @@ + + +This example script shows a significant increase in performance when +using a list, tuple or range over a set in python version 3.11.1. diff --git a/lib/common/pylint_data/messages/use-sequence-for-iteration/good.py b/lib/common/pylint_data/messages/use-sequence-for-iteration/good.py new file mode 100644 index 0000000..c86f85a --- /dev/null +++ b/lib/common/pylint_data/messages/use-sequence-for-iteration/good.py @@ -0,0 +1,5 @@ +for food in ["apples", "lemons", "water"]: + print(f"I like {food}.") + +for food in ("apples", "lemons", "water"): + print(f"I like {food}.") diff --git a/lib/common/pylint_data/messages/use-set-for-membership/bad.py b/lib/common/pylint_data/messages/use-set-for-membership/bad.py new file mode 100644 index 0000000..333754a --- /dev/null +++ b/lib/common/pylint_data/messages/use-set-for-membership/bad.py @@ -0,0 +1,3 @@ +def fruit_is_dangerous_for_cat(fruit: str) -> bool: + """This list is only a silly example, don't make decision regarding your cat diet based on it.""" + return fruit in ["cherry", "grapes"] # [use-set-for-membership] diff --git a/lib/common/pylint_data/messages/use-set-for-membership/good.py b/lib/common/pylint_data/messages/use-set-for-membership/good.py new file mode 100644 index 0000000..e6c7096 --- /dev/null +++ b/lib/common/pylint_data/messages/use-set-for-membership/good.py @@ -0,0 +1,3 @@ +def fruit_is_dangerous_for_cat(fruit: str) -> bool: + """This list is only a silly example, don't make decision regarding your cat diet based on it.""" + return fruit in {"cherry", "grapes"} diff --git a/lib/common/pylint_data/messages/use-symbolic-message-instead/bad.py b/lib/common/pylint_data/messages/use-symbolic-message-instead/bad.py new file mode 100644 index 0000000..649547a --- /dev/null +++ b/lib/common/pylint_data/messages/use-symbolic-message-instead/bad.py @@ -0,0 +1,6 @@ +fruit_name = "plum" + + +# pylint: disable-next=W0621 +def eat(fruit_name: str): # [use-symbolic-message-instead] + ... diff --git a/lib/common/pylint_data/messages/use-symbolic-message-instead/good.py b/lib/common/pylint_data/messages/use-symbolic-message-instead/good.py new file mode 100644 index 0000000..0374b87 --- /dev/null +++ b/lib/common/pylint_data/messages/use-symbolic-message-instead/good.py @@ -0,0 +1,5 @@ +fruit_name = "plum" + + +# pylint: disable-next=redefined-outer-name +def eat(fruit_name: str): ... diff --git a/lib/common/pylint_data/messages/use-yield-from/bad.py b/lib/common/pylint_data/messages/use-yield-from/bad.py new file mode 100644 index 0000000..012bc42 --- /dev/null +++ b/lib/common/pylint_data/messages/use-yield-from/bad.py @@ -0,0 +1,3 @@ +def bad_yield_from(generator): + for item in generator: # [use-yield-from] + yield item diff --git a/lib/common/pylint_data/messages/use-yield-from/details.md b/lib/common/pylint_data/messages/use-yield-from/details.md new file mode 100644 index 0000000..4fd8099 --- /dev/null +++ b/lib/common/pylint_data/messages/use-yield-from/details.md @@ -0,0 +1,17 @@ +`yield from` can be thought of as removing the intermediary (your for +loop) between the function caller and the requested generator. This +enables the caller to directly communicate with the generator (e.g. +using `send()`). This communication is not possible when manually +yielding each element one by one in a loop. + +PEP 380 describes the possibility of adding optimizations specific to +`yield from`. It looks like they have not been implemented as of the +time of writing. Even without said optimizations, the following snippet +shows that `yield from` is marginally faster. + +``` sh +$ python3 -m timeit "def yield_from(): yield from range(100)" "for _ in yield_from(): pass" +100000 loops, best of 5: 2.44 usec per loop +$ python3 -m timeit "def yield_loop():" " for item in range(100): yield item" "for _ in yield_loop(): pass" +100000 loops, best of 5: 2.49 usec per loop +``` diff --git a/lib/common/pylint_data/messages/use-yield-from/good.py b/lib/common/pylint_data/messages/use-yield-from/good.py new file mode 100644 index 0000000..1331a6f --- /dev/null +++ b/lib/common/pylint_data/messages/use-yield-from/good.py @@ -0,0 +1,2 @@ +def good_yield_from(generator): + yield from generator diff --git a/lib/common/pylint_data/messages/use-yield-from/related.md b/lib/common/pylint_data/messages/use-yield-from/related.md new file mode 100644 index 0000000..922bd66 --- /dev/null +++ b/lib/common/pylint_data/messages/use-yield-from/related.md @@ -0,0 +1 @@ +- [PEP 380](https://peps.python.org/pep-0380/) diff --git a/lib/common/pylint_data/messages/used-before-assignment/bad.py b/lib/common/pylint_data/messages/used-before-assignment/bad.py new file mode 100644 index 0000000..6918a07 --- /dev/null +++ b/lib/common/pylint_data/messages/used-before-assignment/bad.py @@ -0,0 +1,2 @@ +print(hello) # [used-before-assignment] +hello = "Hello World !" diff --git a/lib/common/pylint_data/messages/used-before-assignment/good.py b/lib/common/pylint_data/messages/used-before-assignment/good.py new file mode 100644 index 0000000..f5d4d58 --- /dev/null +++ b/lib/common/pylint_data/messages/used-before-assignment/good.py @@ -0,0 +1,2 @@ +hello = "Hello World !" +print(hello) diff --git a/lib/common/pylint_data/messages/used-prior-global-declaration/bad.py b/lib/common/pylint_data/messages/used-prior-global-declaration/bad.py new file mode 100644 index 0000000..804a1f3 --- /dev/null +++ b/lib/common/pylint_data/messages/used-prior-global-declaration/bad.py @@ -0,0 +1,7 @@ +TOMATO = "black cherry" + + +def update_tomato(): + print(TOMATO) # [used-prior-global-declaration] + global TOMATO + TOMATO = "cherry tomato" diff --git a/lib/common/pylint_data/messages/used-prior-global-declaration/good.py b/lib/common/pylint_data/messages/used-prior-global-declaration/good.py new file mode 100644 index 0000000..0736bb4 --- /dev/null +++ b/lib/common/pylint_data/messages/used-prior-global-declaration/good.py @@ -0,0 +1,6 @@ +TOMATO = "black cherry" + + +def update_tomato(): + global TOMATO + TOMATO = "moneymaker" diff --git a/lib/common/pylint_data/messages/useless-else-on-loop/bad.py b/lib/common/pylint_data/messages/useless-else-on-loop/bad.py new file mode 100644 index 0000000..dcd713f --- /dev/null +++ b/lib/common/pylint_data/messages/useless-else-on-loop/bad.py @@ -0,0 +1,6 @@ +def find_even_number(numbers): + for x in numbers: + if x % 2 == 0: + return x + else: # [useless-else-on-loop] + print("Did not find an even number") diff --git a/lib/common/pylint_data/messages/useless-else-on-loop/good.py b/lib/common/pylint_data/messages/useless-else-on-loop/good.py new file mode 100644 index 0000000..a0bf9cd --- /dev/null +++ b/lib/common/pylint_data/messages/useless-else-on-loop/good.py @@ -0,0 +1,5 @@ +def find_even_number(numbers): + for x in numbers: + if x % 2 == 0: + return x + print("Did not find an even number") diff --git a/lib/common/pylint_data/messages/useless-import-alias/bad.py b/lib/common/pylint_data/messages/useless-import-alias/bad.py new file mode 100644 index 0000000..66c25dc --- /dev/null +++ b/lib/common/pylint_data/messages/useless-import-alias/bad.py @@ -0,0 +1 @@ +import pandas as pandas # [useless-import-alias] diff --git a/lib/common/pylint_data/messages/useless-import-alias/details.md b/lib/common/pylint_data/messages/useless-import-alias/details.md new file mode 100644 index 0000000..0d103ee --- /dev/null +++ b/lib/common/pylint_data/messages/useless-import-alias/details.md @@ -0,0 +1,8 @@ +# Known issue + +If you prefer to use \"from-as\" to explicitly reexport in API +(`from fruit import orange as orange`) instead of using `__all__` this +message will be a false positive. + +Use `--allow-reexport-from-package` to allow explicit reexports by alias +in package `__init__` files. diff --git a/lib/common/pylint_data/messages/useless-import-alias/good.py b/lib/common/pylint_data/messages/useless-import-alias/good.py new file mode 100644 index 0000000..ffd919b --- /dev/null +++ b/lib/common/pylint_data/messages/useless-import-alias/good.py @@ -0,0 +1 @@ +import pandas as pd diff --git a/lib/common/pylint_data/messages/useless-import-alias/related.md b/lib/common/pylint_data/messages/useless-import-alias/related.md new file mode 100644 index 0000000..57f8eb1 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-import-alias/related.md @@ -0,0 +1,6 @@ +- `--allow-reexport-from-package`{.interpreted-text + role="ref"} +- [PEP 8, Import Guideline](https://peps.python.org/pep-0008/#imports) +- `Pylint block-disable `{.interpreted-text role="ref"} +- [mypy + \--no-implicit-reexport](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-no-implicit-reexport) diff --git a/lib/common/pylint_data/messages/useless-object-inheritance/bad.py b/lib/common/pylint_data/messages/useless-object-inheritance/bad.py new file mode 100644 index 0000000..2b6cca4 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-object-inheritance/bad.py @@ -0,0 +1,2 @@ +class Banana(object): # [useless-object-inheritance] + ... diff --git a/lib/common/pylint_data/messages/useless-object-inheritance/good.py b/lib/common/pylint_data/messages/useless-object-inheritance/good.py new file mode 100644 index 0000000..fc33e9e --- /dev/null +++ b/lib/common/pylint_data/messages/useless-object-inheritance/good.py @@ -0,0 +1 @@ +class Banana: ... diff --git a/lib/common/pylint_data/messages/useless-option-value/bad.py b/lib/common/pylint_data/messages/useless-option-value/bad.py new file mode 100644 index 0000000..e284b3f --- /dev/null +++ b/lib/common/pylint_data/messages/useless-option-value/bad.py @@ -0,0 +1,3 @@ +"""'bad-continuation' was removed from pylint in https://github.com/pylint-dev/pylint/pull/3571""" + +# pylint: disable=bad-continuation # [useless-option-value] diff --git a/lib/common/pylint_data/messages/useless-option-value/details.md b/lib/common/pylint_data/messages/useless-option-value/details.md new file mode 100644 index 0000000..d89ca20 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-option-value/details.md @@ -0,0 +1,2 @@ +You can disable this check if you don\'t want to cleanup your +configuration of old messages. diff --git a/lib/common/pylint_data/messages/useless-option-value/good.py b/lib/common/pylint_data/messages/useless-option-value/good.py new file mode 100644 index 0000000..619b5b6 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-option-value/good.py @@ -0,0 +1 @@ +"""'bad-continuation' was removed from pylint in https://github.com/pylint-dev/pylint/pull/3571""" diff --git a/lib/common/pylint_data/messages/useless-param-doc/bad.py b/lib/common/pylint_data/messages/useless-param-doc/bad.py new file mode 100644 index 0000000..f6c289d --- /dev/null +++ b/lib/common/pylint_data/messages/useless-param-doc/bad.py @@ -0,0 +1,7 @@ +def say_hello(_new: str) -> str: # [useless-param-doc] + """say hello! + + :param _new: + :return: comment + """ + return "hello" diff --git a/lib/common/pylint_data/messages/useless-param-doc/good.py b/lib/common/pylint_data/messages/useless-param-doc/good.py new file mode 100644 index 0000000..58d8fa0 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-param-doc/good.py @@ -0,0 +1,6 @@ +def say_hello(_new: str) -> str: + """say hello! + + :return: comment + """ + return "hello" diff --git a/lib/common/pylint_data/messages/useless-parent-delegation/bad.py b/lib/common/pylint_data/messages/useless-parent-delegation/bad.py new file mode 100644 index 0000000..5132e35 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-parent-delegation/bad.py @@ -0,0 +1,8 @@ +class Animal: + def eat(self, food): + print(f"Eating {food}") + + +class Human(Animal): + def eat(self, food): # [useless-parent-delegation] + super(Human, self).eat(food) diff --git a/lib/common/pylint_data/messages/useless-parent-delegation/good.py b/lib/common/pylint_data/messages/useless-parent-delegation/good.py new file mode 100644 index 0000000..890a431 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-parent-delegation/good.py @@ -0,0 +1,7 @@ +class Animal: + def eat(self, food): + print(f"Eating {food}") + + +class Human(Animal): + """There is no need to override 'eat' it has the same signature as the implementation in Animal.""" diff --git a/lib/common/pylint_data/messages/useless-parent-delegation/related.md b/lib/common/pylint_data/messages/useless-parent-delegation/related.md new file mode 100644 index 0000000..856b3a0 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-parent-delegation/related.md @@ -0,0 +1,2 @@ +- [Stackoverflow explanation for + \'useless-super-delegation\'](https://stackoverflow.com/a/51030674/2519059) diff --git a/lib/common/pylint_data/messages/useless-return/bad.py b/lib/common/pylint_data/messages/useless-return/bad.py new file mode 100644 index 0000000..198d8ed --- /dev/null +++ b/lib/common/pylint_data/messages/useless-return/bad.py @@ -0,0 +1,6 @@ +import sys + + +def print_python_version(): # [useless-return] + print(sys.version) + return None diff --git a/lib/common/pylint_data/messages/useless-return/good.py b/lib/common/pylint_data/messages/useless-return/good.py new file mode 100644 index 0000000..eeb60cb --- /dev/null +++ b/lib/common/pylint_data/messages/useless-return/good.py @@ -0,0 +1,5 @@ +import sys + + +def print_python_version(): + print(sys.version) diff --git a/lib/common/pylint_data/messages/useless-suppression/bad.py b/lib/common/pylint_data/messages/useless-suppression/bad.py new file mode 100644 index 0000000..b343b63 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-suppression/bad.py @@ -0,0 +1,6 @@ +fruit_counter = 0 + + +# pylint: disable-next=redefined-outer-name +def eat(fruit_name: str): # [useless-suppression] + ... diff --git a/lib/common/pylint_data/messages/useless-suppression/good.py b/lib/common/pylint_data/messages/useless-suppression/good.py new file mode 100644 index 0000000..faa5e65 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-suppression/good.py @@ -0,0 +1,4 @@ +fruit_counter = 0 + + +def eat(fruit_name: str): ... diff --git a/lib/common/pylint_data/messages/useless-type-doc/bad.py b/lib/common/pylint_data/messages/useless-type-doc/bad.py new file mode 100644 index 0000000..dd6393f --- /dev/null +++ b/lib/common/pylint_data/messages/useless-type-doc/bad.py @@ -0,0 +1,8 @@ +def print_fruit(fruit, _): # [useless-type-doc] + """docstring ... + + Args: + fruit (str): A fruit. + _ (float): Another argument. + """ + print(fruit) diff --git a/lib/common/pylint_data/messages/useless-type-doc/good.py b/lib/common/pylint_data/messages/useless-type-doc/good.py new file mode 100644 index 0000000..f3ee96a --- /dev/null +++ b/lib/common/pylint_data/messages/useless-type-doc/good.py @@ -0,0 +1,7 @@ +def print_fruit(fruit): + """docstring ... + + Args: + fruit (str): A fruit. + """ + print(fruit) diff --git a/lib/common/pylint_data/messages/useless-with-lock/bad.py b/lib/common/pylint_data/messages/useless-with-lock/bad.py new file mode 100644 index 0000000..cb7f0d8 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-with-lock/bad.py @@ -0,0 +1,6 @@ +import threading + +with threading.Lock(): # [useless-with-lock] + print("Make your bed.") +with threading.Lock(): # [useless-with-lock] + print("Sleep in it") diff --git a/lib/common/pylint_data/messages/useless-with-lock/good.py b/lib/common/pylint_data/messages/useless-with-lock/good.py new file mode 100644 index 0000000..86b1049 --- /dev/null +++ b/lib/common/pylint_data/messages/useless-with-lock/good.py @@ -0,0 +1,7 @@ +import threading + +lock = threading.Lock() +with lock: + print("Make your bed.") +with lock: + print("Sleep in it.") diff --git a/lib/common/pylint_data/messages/using-assignment-expression-in-unsupported-version/bad.py b/lib/common/pylint_data/messages/using-assignment-expression-in-unsupported-version/bad.py new file mode 100644 index 0000000..ca4e4a6 --- /dev/null +++ b/lib/common/pylint_data/messages/using-assignment-expression-in-unsupported-version/bad.py @@ -0,0 +1,5 @@ +import random + +# +1: [using-assignment-expression-in-unsupported-version] +if zero_or_one := random.randint(0, 1): + assert zero_or_one == 1 diff --git a/lib/common/pylint_data/messages/using-assignment-expression-in-unsupported-version/details.md b/lib/common/pylint_data/messages/using-assignment-expression-in-unsupported-version/details.md new file mode 100644 index 0000000..33bcca5 --- /dev/null +++ b/lib/common/pylint_data/messages/using-assignment-expression-in-unsupported-version/details.md @@ -0,0 +1,3 @@ +The assignment expression (walrus) operator ([:=]{.title-ref}) was +introduced in Python 3.8; to use it, please use a more recent version of +Python. diff --git a/lib/common/pylint_data/messages/using-assignment-expression-in-unsupported-version/good.py b/lib/common/pylint_data/messages/using-assignment-expression-in-unsupported-version/good.py new file mode 100644 index 0000000..a31a74a --- /dev/null +++ b/lib/common/pylint_data/messages/using-assignment-expression-in-unsupported-version/good.py @@ -0,0 +1,5 @@ +import random + +zero_or_one = random.randint(0, 1) +if zero_or_one: + assert zero_or_one == 1 diff --git a/lib/common/pylint_data/messages/using-constant-test/bad.py b/lib/common/pylint_data/messages/using-constant-test/bad.py new file mode 100644 index 0000000..a995d78 --- /dev/null +++ b/lib/common/pylint_data/messages/using-constant-test/bad.py @@ -0,0 +1,4 @@ +if 0: # [using-constant-test] + print("This code is never executed.") +if 1: # [using-constant-test] + print("This code is always executed.") diff --git a/lib/common/pylint_data/messages/using-constant-test/good.py b/lib/common/pylint_data/messages/using-constant-test/good.py new file mode 100644 index 0000000..c8441a9 --- /dev/null +++ b/lib/common/pylint_data/messages/using-constant-test/good.py @@ -0,0 +1 @@ +print("This code is always executed.") diff --git a/lib/common/pylint_data/messages/using-exception-groups-in-unsupported-version/bad.py b/lib/common/pylint_data/messages/using-exception-groups-in-unsupported-version/bad.py new file mode 100644 index 0000000..c225e6e --- /dev/null +++ b/lib/common/pylint_data/messages/using-exception-groups-in-unsupported-version/bad.py @@ -0,0 +1,12 @@ +def f(): + excs = [OSError("error 1"), SystemError("error 2")] + # +1: [using-exception-groups-in-unsupported-version] + raise ExceptionGroup("there were problems", excs) + + +try: # [using-exception-groups-in-unsupported-version] + f() +except* OSError as e: + print("There were OSErrors") +except* SystemError as e: + print("There were SystemErrors") diff --git a/lib/common/pylint_data/messages/using-exception-groups-in-unsupported-version/details.md b/lib/common/pylint_data/messages/using-exception-groups-in-unsupported-version/details.md new file mode 100644 index 0000000..6e0a49f --- /dev/null +++ b/lib/common/pylint_data/messages/using-exception-groups-in-unsupported-version/details.md @@ -0,0 +1,2 @@ +Exception groups were introduced in Python 3.11; to use it, please use a +more recent version of Python. diff --git a/lib/common/pylint_data/messages/using-exception-groups-in-unsupported-version/good.py b/lib/common/pylint_data/messages/using-exception-groups-in-unsupported-version/good.py new file mode 100644 index 0000000..e7ac7a5 --- /dev/null +++ b/lib/common/pylint_data/messages/using-exception-groups-in-unsupported-version/good.py @@ -0,0 +1,10 @@ +def f(): + raise OSError("error 1") + + +try: + f() +except OSError as e: + print("There were OSErrors") +except SystemError as e: + print("There were SystemErrors") diff --git a/lib/common/pylint_data/messages/using-f-string-in-unsupported-version/bad.py b/lib/common/pylint_data/messages/using-f-string-in-unsupported-version/bad.py new file mode 100644 index 0000000..0fa2333 --- /dev/null +++ b/lib/common/pylint_data/messages/using-f-string-in-unsupported-version/bad.py @@ -0,0 +1 @@ +f"python {3.5} is past end of life" # [using-f-string-in-unsupported-version] diff --git a/lib/common/pylint_data/messages/using-f-string-in-unsupported-version/details.md b/lib/common/pylint_data/messages/using-f-string-in-unsupported-version/details.md new file mode 100644 index 0000000..bca55a2 --- /dev/null +++ b/lib/common/pylint_data/messages/using-f-string-in-unsupported-version/details.md @@ -0,0 +1,2 @@ +f-strings were introduced in Python version 3.6; to use them, please use +a more recent version of Python. diff --git a/lib/common/pylint_data/messages/using-f-string-in-unsupported-version/good.py b/lib/common/pylint_data/messages/using-f-string-in-unsupported-version/good.py new file mode 100644 index 0000000..695e0ab --- /dev/null +++ b/lib/common/pylint_data/messages/using-f-string-in-unsupported-version/good.py @@ -0,0 +1 @@ +"python {} is past end of life".format(3.5) diff --git a/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/bad.py b/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/bad.py new file mode 100644 index 0000000..4175532 --- /dev/null +++ b/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/bad.py @@ -0,0 +1,7 @@ +from typing import final + + +@final # [using-final-decorator-in-unsupported-version] +class Playtypus(Animal): + @final # [using-final-decorator-in-unsupported-version] + def lay_egg(self): ... diff --git a/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/details.md b/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/details.md new file mode 100644 index 0000000..61fe58d --- /dev/null +++ b/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/details.md @@ -0,0 +1,3 @@ +The message is emitted when the `final` decorator is used with a Python +version less than 3.8. The `final` decorator was introduced in Python +version 3.8. diff --git a/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/good.py b/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/good.py new file mode 100644 index 0000000..70f742d --- /dev/null +++ b/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/good.py @@ -0,0 +1,2 @@ +class Playtypus(Animal): + def lay_egg(self): ... diff --git a/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/related.md b/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/related.md new file mode 100644 index 0000000..28c9e01 --- /dev/null +++ b/lib/common/pylint_data/messages/using-final-decorator-in-unsupported-version/related.md @@ -0,0 +1 @@ +- [PEP 591](https://peps.python.org/pep-0591/#the-final-decorator) diff --git a/lib/common/pylint_data/messages/using-generic-type-syntax-in-unsupported-version/bad.py b/lib/common/pylint_data/messages/using-generic-type-syntax-in-unsupported-version/bad.py new file mode 100644 index 0000000..b47bddd --- /dev/null +++ b/lib/common/pylint_data/messages/using-generic-type-syntax-in-unsupported-version/bad.py @@ -0,0 +1 @@ +type Vector = list[float] # [using-generic-type-syntax-in-unsupported-version] diff --git a/lib/common/pylint_data/messages/using-generic-type-syntax-in-unsupported-version/details.md b/lib/common/pylint_data/messages/using-generic-type-syntax-in-unsupported-version/details.md new file mode 100644 index 0000000..44cfdf1 --- /dev/null +++ b/lib/common/pylint_data/messages/using-generic-type-syntax-in-unsupported-version/details.md @@ -0,0 +1,2 @@ +Generic type syntax was introduced in Python 3.12; to use it, please use +a more recent version of Python. diff --git a/lib/common/pylint_data/messages/using-generic-type-syntax-in-unsupported-version/good.py b/lib/common/pylint_data/messages/using-generic-type-syntax-in-unsupported-version/good.py new file mode 100644 index 0000000..3f80b01 --- /dev/null +++ b/lib/common/pylint_data/messages/using-generic-type-syntax-in-unsupported-version/good.py @@ -0,0 +1,3 @@ +from typing import TypeAlias + +Vector: TypeAlias = list[float] diff --git a/lib/common/pylint_data/messages/using-positional-only-args-in-unsupported-version/bad.py b/lib/common/pylint_data/messages/using-positional-only-args-in-unsupported-version/bad.py new file mode 100644 index 0000000..3923db1 --- /dev/null +++ b/lib/common/pylint_data/messages/using-positional-only-args-in-unsupported-version/bad.py @@ -0,0 +1,2 @@ +def add(x, y, /): # [using-positional-only-args-in-unsupported-version] + return x + y diff --git a/lib/common/pylint_data/messages/using-positional-only-args-in-unsupported-version/details.md b/lib/common/pylint_data/messages/using-positional-only-args-in-unsupported-version/details.md new file mode 100644 index 0000000..c0d621c --- /dev/null +++ b/lib/common/pylint_data/messages/using-positional-only-args-in-unsupported-version/details.md @@ -0,0 +1,2 @@ +Positional-only arguments were introduced in Python 3.8; to use them, +please use a more recent version of Python. diff --git a/lib/common/pylint_data/messages/using-positional-only-args-in-unsupported-version/good.py b/lib/common/pylint_data/messages/using-positional-only-args-in-unsupported-version/good.py new file mode 100644 index 0000000..bfc542d --- /dev/null +++ b/lib/common/pylint_data/messages/using-positional-only-args-in-unsupported-version/good.py @@ -0,0 +1,3 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring +def add(x, y): + return x + y diff --git a/lib/common/pylint_data/messages/while-used/bad.py b/lib/common/pylint_data/messages/while-used/bad.py new file mode 100644 index 0000000..44e8df8 --- /dev/null +++ b/lib/common/pylint_data/messages/while-used/bad.py @@ -0,0 +1,12 @@ +import requests + + +def fetch_data(): + i = 1 + while i < 6: # [while-used] + print(f"Attempt {i}...") + try: + return requests.get("https://example.com/data") + except requests.exceptions.RequestException: + pass + i += 1 diff --git a/lib/common/pylint_data/messages/while-used/good.py b/lib/common/pylint_data/messages/while-used/good.py new file mode 100644 index 0000000..762d738 --- /dev/null +++ b/lib/common/pylint_data/messages/while-used/good.py @@ -0,0 +1,10 @@ +import requests + + +def fetch_data(): + for i in range(1, 6): + print(f"Attempt {i}...") + try: + return requests.get("https://example.com/data") + except requests.exceptions.RequestException: + pass diff --git a/lib/common/pylint_data/messages/while-used/related.md b/lib/common/pylint_data/messages/while-used/related.md new file mode 100644 index 0000000..b018c17 --- /dev/null +++ b/lib/common/pylint_data/messages/while-used/related.md @@ -0,0 +1,2 @@ +- [Stackoverflow + discussion](https://stackoverflow.com/questions/920645/when-to-use-while-or-for-in-python) diff --git a/lib/common/pylint_data/messages/wildcard-import/bad.py b/lib/common/pylint_data/messages/wildcard-import/bad.py new file mode 100644 index 0000000..b0d1811 --- /dev/null +++ b/lib/common/pylint_data/messages/wildcard-import/bad.py @@ -0,0 +1 @@ +from abc import * # [wildcard-import] diff --git a/lib/common/pylint_data/messages/wildcard-import/good.py b/lib/common/pylint_data/messages/wildcard-import/good.py new file mode 100644 index 0000000..d098e4b --- /dev/null +++ b/lib/common/pylint_data/messages/wildcard-import/good.py @@ -0,0 +1,4 @@ +# Either import module or +# only import required objects from module. +import abc +from abc import ABC, abstractmethod diff --git a/lib/common/pylint_data/messages/wrong-exception-operation/bad.py b/lib/common/pylint_data/messages/wrong-exception-operation/bad.py new file mode 100644 index 0000000..e4aff5d --- /dev/null +++ b/lib/common/pylint_data/messages/wrong-exception-operation/bad.py @@ -0,0 +1,4 @@ +try: + 1 / 0 +except ValueError + TypeError: # [wrong-exception-operation] + pass diff --git a/lib/common/pylint_data/messages/wrong-exception-operation/good.py b/lib/common/pylint_data/messages/wrong-exception-operation/good.py new file mode 100644 index 0000000..b5170dd --- /dev/null +++ b/lib/common/pylint_data/messages/wrong-exception-operation/good.py @@ -0,0 +1,4 @@ +try: + 1 / 0 +except (ValueError, TypeError): + pass diff --git a/lib/common/pylint_data/messages/wrong-import-order/bad.py b/lib/common/pylint_data/messages/wrong-import-order/bad.py new file mode 100644 index 0000000..3301d81 --- /dev/null +++ b/lib/common/pylint_data/messages/wrong-import-order/bad.py @@ -0,0 +1,4 @@ +import os +from . import utils +import pylint # [wrong-import-order] +import sys # [wrong-import-order] diff --git a/lib/common/pylint_data/messages/wrong-import-order/good.py b/lib/common/pylint_data/messages/wrong-import-order/good.py new file mode 100644 index 0000000..853f8ff --- /dev/null +++ b/lib/common/pylint_data/messages/wrong-import-order/good.py @@ -0,0 +1,6 @@ +import os +import sys + +import pylint + +from . import utils diff --git a/lib/common/pylint_data/messages/wrong-import-position/bad.py b/lib/common/pylint_data/messages/wrong-import-position/bad.py new file mode 100644 index 0000000..38e442e --- /dev/null +++ b/lib/common/pylint_data/messages/wrong-import-position/bad.py @@ -0,0 +1,7 @@ +import os + +home = os.environ["HOME"] + +import sys # [wrong-import-position] + +print(f"Home directory is {home}", file=sys.stderr) diff --git a/lib/common/pylint_data/messages/wrong-import-position/good.py b/lib/common/pylint_data/messages/wrong-import-position/good.py new file mode 100644 index 0000000..37330eb --- /dev/null +++ b/lib/common/pylint_data/messages/wrong-import-position/good.py @@ -0,0 +1,5 @@ +import os +import sys + +home = os.environ["HOME"] +print(f"Home directory is {home}", file=sys.stderr) diff --git a/lib/common/pylint_data/messages/wrong-spelling-in-comment/bad.py b/lib/common/pylint_data/messages/wrong-spelling-in-comment/bad.py new file mode 100644 index 0000000..becaf40 --- /dev/null +++ b/lib/common/pylint_data/messages/wrong-spelling-in-comment/bad.py @@ -0,0 +1 @@ +# There's a mistkae in this string # [wrong-spelling-in-comment] diff --git a/lib/common/pylint_data/messages/wrong-spelling-in-comment/good.py b/lib/common/pylint_data/messages/wrong-spelling-in-comment/good.py new file mode 100644 index 0000000..84af67f --- /dev/null +++ b/lib/common/pylint_data/messages/wrong-spelling-in-comment/good.py @@ -0,0 +1 @@ +# There's no mistake in this string diff --git a/lib/common/pylint_data/messages/wrong-spelling-in-docstring/bad.py b/lib/common/pylint_data/messages/wrong-spelling-in-docstring/bad.py new file mode 100644 index 0000000..b5c9149 --- /dev/null +++ b/lib/common/pylint_data/messages/wrong-spelling-in-docstring/bad.py @@ -0,0 +1 @@ +"""There's a mistkae in this string""" # [wrong-spelling-in-docstring] diff --git a/lib/common/pylint_data/messages/wrong-spelling-in-docstring/good.py b/lib/common/pylint_data/messages/wrong-spelling-in-docstring/good.py new file mode 100644 index 0000000..10d19e9 --- /dev/null +++ b/lib/common/pylint_data/messages/wrong-spelling-in-docstring/good.py @@ -0,0 +1 @@ +"""There's no mistake in this string""" diff --git a/lib/common/pylint_data/messages/yield-inside-async-function/bad.py b/lib/common/pylint_data/messages/yield-inside-async-function/bad.py new file mode 100644 index 0000000..6e1d6bd --- /dev/null +++ b/lib/common/pylint_data/messages/yield-inside-async-function/bad.py @@ -0,0 +1,2 @@ +async def foo(): + yield from [1, 2, 3] # [yield-inside-async-function] diff --git a/lib/common/pylint_data/messages/yield-inside-async-function/details.md b/lib/common/pylint_data/messages/yield-inside-async-function/details.md new file mode 100644 index 0000000..e9afa69 --- /dev/null +++ b/lib/common/pylint_data/messages/yield-inside-async-function/details.md @@ -0,0 +1 @@ +The message can\'t be emitted when using Python \< 3.5. diff --git a/lib/common/pylint_data/messages/yield-inside-async-function/good.py b/lib/common/pylint_data/messages/yield-inside-async-function/good.py new file mode 100644 index 0000000..1af9650 --- /dev/null +++ b/lib/common/pylint_data/messages/yield-inside-async-function/good.py @@ -0,0 +1,7 @@ +async def foo(): + def _inner_foo(): + yield from [1, 2, 3] + + +async def foo(): + yield 42 diff --git a/lib/common/pylint_data/messages/yield-inside-async-function/related.md b/lib/common/pylint_data/messages/yield-inside-async-function/related.md new file mode 100644 index 0000000..1b44cc9 --- /dev/null +++ b/lib/common/pylint_data/messages/yield-inside-async-function/related.md @@ -0,0 +1 @@ +- [PEP 525](https://peps.python.org/pep-0525/) diff --git a/lib/common/pylint_data/messages/yield-outside-function/bad.py b/lib/common/pylint_data/messages/yield-outside-function/bad.py new file mode 100644 index 0000000..c4bff8c --- /dev/null +++ b/lib/common/pylint_data/messages/yield-outside-function/bad.py @@ -0,0 +1,2 @@ +for i in range(10): + yield i # [yield-outside-function] diff --git a/lib/common/pylint_data/messages/yield-outside-function/good.py b/lib/common/pylint_data/messages/yield-outside-function/good.py new file mode 100644 index 0000000..4d00779 --- /dev/null +++ b/lib/common/pylint_data/messages/yield-outside-function/good.py @@ -0,0 +1,3 @@ +def one_to_ten(): + for i in range(10): + yield i diff --git a/lib/currency-exchange/analyzer.py b/lib/currency-exchange/analyzer.py index 42a45ed..6bda1db 100644 --- a/lib/currency-exchange/analyzer.py +++ b/lib/currency-exchange/analyzer.py @@ -4,8 +4,12 @@ """ import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -15,7 +19,9 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") def analyze(in_path: Path, out_path: Path): @@ -53,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/ellens-alien-game/.pylintrc b/lib/ellens-alien-game/.pylintrc deleted file mode 100644 index e927745..0000000 --- a/lib/ellens-alien-game/.pylintrc +++ /dev/null @@ -1,381 +0,0 @@ -########################################################################### -# This pylintrc file contains a best-effort configuration for the concept -# exercise Ellen's Alien Game on the Python track of exercism.org. -# -# Regarding single letter variable names: this Stack Overflow discussion -# https://stackoverflow.com/questions/21833872/why-does-pylint-object-to-single-character-variable-names -# explains why this is enforced. -# -# **PLEASE NOTE** -# Since this exercise covers the use of 'pass' as well as the use of class -# attributes, we have disabled 'unnecessary-pass' and 'attribute-defined-outside-init' -# and may disable similar checks in the future. -########################################################################### - - -[MASTER] - -# Files or directories to be skipped. They should be base names, not paths. -ignore=third_party - -# Files or directories matching the regex patterns are skipped. The regex -# matches against base names, not paths. -ignore-patterns= - -# Pickle collected data for later comparisons. -persistent=no - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Use multiple processes to speed up Pylint. -jobs=4 - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then re-enable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" - -# As of Pylint 2.6+, the following options are not supported, so they're commented out: -# -# misplaced-comparison-constant -# relative-import -# input-builtin -# inconsistent-return-statements -# no-absolute-import -# raising-string -# round-builtin - -disable=arguments-differ, - attribute-defined-outside-init, - fixme, - global-statement, - implicit-str-concat-in-sequence, - import-error, - import-self, - locally-disabled, - no-else-break, - no-else-continue, - no-else-raise, - no-else-return, - no-member, - no-name-in-module, - signature-differs, - suppressed-message, - too-many-boolean-expressions, - too-many-branches, - too-many-locals, - too-many-public-methods, - too-many-return-statements, - too-many-statements, - unnecessary-pass, - unused-argument, - useless-suppression - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -# output-format=colorized - -# Tells whether to display a full report or only the messages -reports=no - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - - -[BASIC] - -# Good variable names which should always be accepted, separated by a comma -good-names=main,_, item, element, index - -# Bad variable names which should always be refused, separated by a comma -bad-names=x,y,i,l,L,O,j,m,n,k - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=yes - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl - -# Regular expression matching correct function names -function-rgx=^(?:(?PsetUp|tearDown|setUpModule|tearDownModule)|(?P_?[A-Z][a-zA-Z0-9]*)|(?P_?[a-z][a-z0-9_]*))$ - -# Regular expression matching correct variable names -variable-rgx=^[a-z_][a-z0-9_]{1,30}$ - -# Regular expression matching correct constant names -const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ - -# Regular expression matching correct attribute names -attr-rgx=^_{0,2}[a-z][a-z0-9_]*$ - -# Regular expression matching correct argument names -argument-rgx=^[a-z_][a-z0-9_]{1,30}$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct class names -class-rgx=^_?[A-Z][a-zA-Z0-9]*$ - -# Regular expression matching correct module names -module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$ - -# Regular expression matching correct method names -method-rgx=(?x)^(?:(?P_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P_{0,2}[a-z][a-z0-9_]*))$ - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=10 - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=120 - -# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt -# lines made too long by directives to pytype. - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=(?x)( - ^\s*(\#\ )??$| - ^\s*(from\s+\S+\s+)?import\s+.+$) - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=yes - -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -# As of Pylint 2.6+, this option has been disabled, so this is commented out. -#no-space-check= - -# Maximum number of lines in a module -max-module-lines=99999 - -# String used as indentation unit. Currently 4, consistent with -# PEP 8. -indent-string=' ' - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=TODO - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=yes - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks= - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging,absl.logging - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub, - TERMIOS, - Bastion, - rexec, - sets - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant, absl - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls, - class_ - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=StandardError, - Exception, - BaseException diff --git a/lib/ellens-alien-game/analyzer.py b/lib/ellens-alien-game/analyzer.py index a311657..8f35024 100644 --- a/lib/ellens-alien-game/analyzer.py +++ b/lib/ellens-alien-game/analyzer.py @@ -4,8 +4,12 @@ """ import ast + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path -from pylint import epylint as lint +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -15,13 +19,16 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + def analyze(in_path: Path, out_path: Path): - """Analyze the user's solution and give feedback. + """ + Analyze the user's Two Fer solution to give feedback. Outputs a JSON that - Outputs a JSON that conforms to - https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#output-format + conforms to https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#output-format """ # List of Comment objects to process @@ -50,6 +57,10 @@ def analyze(in_path: Path, out_path: Path): return Analysis.require(comments).dump(output_file) # Generate PyLint comments for additional feedback. - comments.extend(generate_pylint_comments(in_path, pylint_spec='/opt/analyzer/lib/ellens-alien-game/.pylintrc')) + comments.extend(generate_pylint_comments(in_path)) + + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) return Analysis.summarize_comments(comments, output_file) diff --git a/lib/ghost-gobble-arcade-game/analyzer.py b/lib/ghost-gobble-arcade-game/analyzer.py index 2d4c8bc..ce53a4b 100644 --- a/lib/ghost-gobble-arcade-game/analyzer.py +++ b/lib/ghost-gobble-arcade-game/analyzer.py @@ -4,8 +4,12 @@ """ import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -15,7 +19,9 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") def analyze(in_path: Path, out_path: Path): @@ -53,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/guidos-gorgeous-lasagna/analyzer.py b/lib/guidos-gorgeous-lasagna/analyzer.py index 479e335..22f7cd3 100644 --- a/lib/guidos-gorgeous-lasagna/analyzer.py +++ b/lib/guidos-gorgeous-lasagna/analyzer.py @@ -4,8 +4,12 @@ """ import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -15,7 +19,9 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") def analyze(in_path: Path, out_path: Path): @@ -53,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/inventory-management/analyzer.py b/lib/inventory-management/analyzer.py index f113145..9b55389 100644 --- a/lib/inventory-management/analyzer.py +++ b/lib/inventory-management/analyzer.py @@ -4,8 +4,12 @@ """ import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -15,7 +19,9 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") def analyze(in_path: Path, out_path: Path): @@ -53,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/little-sisters-essay/analyzer.py b/lib/little-sisters-essay/analyzer.py index b4a9fd1..b44fd10 100644 --- a/lib/little-sisters-essay/analyzer.py +++ b/lib/little-sisters-essay/analyzer.py @@ -2,9 +2,14 @@ Analyzer for the `little-sisters-essay` exercise. Only Pylint checks are currently active. """ + import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -14,7 +19,10 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + def analyze(in_path: Path, out_path: Path): """ @@ -51,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/little-sisters-vocab/analyzer.py b/lib/little-sisters-vocab/analyzer.py index ed1b36c..bb79f71 100644 --- a/lib/little-sisters-vocab/analyzer.py +++ b/lib/little-sisters-vocab/analyzer.py @@ -2,9 +2,14 @@ Analyzer for the `little-sisters-vocab` exercise. Only Pylint checks are currently active. """ + import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -14,7 +19,10 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + def analyze(in_path: Path, out_path: Path): """ @@ -51,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/log-levels/analyzer.py b/lib/log-levels/analyzer.py new file mode 100644 index 0000000..7e8802e --- /dev/null +++ b/lib/log-levels/analyzer.py @@ -0,0 +1,65 @@ +""" +Analyzer for the `log-levels` exercise. +""" + +import ast + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint +from pathlib import Path +from pylint.reporters.text import TextReporter + +from common import Analysis, BaseFeedback, Summary +from common.comment import Comment, CommentTypes +from common.pylint_comments import generate_pylint_comments + + +class Comments(BaseFeedback): + NO_MODULE = ("general", "no_module") + NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") + MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + + +def analyze(in_path: Path, out_path: Path): + """ + Analyze the user's Two Fer solution to give feedback. Outputs a JSON that + + conforms to https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#output-format + """ + + # List of Comment objects to process + comments = [] + + output_file = out_path.parent.joinpath("analysis.json") + + # input file - if it can't be found, fail and bail + try: + user_solution = in_path.read_text() + except OSError as err: + # fail out fast with an ESSENTIAL (required) type comment for the student + comments.append(Comment(type=CommentTypes.ESSENTIAL, params={}, comment=Comments.MALFORMED_CODE)) + finally: + if comments: + return Analysis.require(comments).dump(output_file) + + # AST - if an AST can't be made, fail and bail + try: + tree = ast.parse(user_solution) + except Exception: + # If ast.parse fails, assume malformed code and fail with an ESSENTIAL (required) type comment for the student + comments.append(Comment(type=CommentTypes.ESSENTIAL, params={}, comment=Comments.MALFORMED_CODE)) + finally: + if comments: + return Analysis.require(comments).dump(output_file) + + # Generate PyLint comments for additional feedback. + comments.extend(generate_pylint_comments(in_path)) + + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/making-the-grade/analyzer.py b/lib/making-the-grade/analyzer.py index 8691bef..edb3722 100644 --- a/lib/making-the-grade/analyzer.py +++ b/lib/making-the-grade/analyzer.py @@ -2,9 +2,14 @@ Analyzer for the `making-the-grade` exercise. Only Pylint checks are currently active. """ + import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -14,7 +19,10 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + def analyze(in_path: Path, out_path: Path): """ @@ -51,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/meltdown-mitigation/analyzer.py b/lib/meltdown-mitigation/analyzer.py index 566fd7b..18a7a2d 100644 --- a/lib/meltdown-mitigation/analyzer.py +++ b/lib/meltdown-mitigation/analyzer.py @@ -2,9 +2,14 @@ Analyzer for the `Meltdown-mitigation` exercise. Only Pylint checks are currently active. """ + import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -14,7 +19,10 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + def analyze(in_path: Path, out_path: Path): """ @@ -51,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/pretty-leaflet/analyzer.py b/lib/pretty-leaflet/analyzer.py new file mode 100644 index 0000000..6136750 --- /dev/null +++ b/lib/pretty-leaflet/analyzer.py @@ -0,0 +1,65 @@ +""" +Analyzer for the `cater-waiter` exercise. +""" + +import ast + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint +from pathlib import Path +from pylint.reporters.text import TextReporter + +from common import Analysis, BaseFeedback, Summary +from common.comment import Comment, CommentTypes +from common.pylint_comments import generate_pylint_comments + + +class Comments(BaseFeedback): + NO_MODULE = ("general", "no_module") + NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") + MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + + +def analyze(in_path: Path, out_path: Path): + """ + Analyze the user's Two Fer solution to give feedback. Outputs a JSON that + + conforms to https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#output-format + """ + + # List of Comment objects to process + comments = [] + + output_file = out_path.parent.joinpath("analysis.json") + + # input file - if it can't be found, fail and bail + try: + user_solution = in_path.read_text() + except OSError as err: + # fail out fast with an ESSENTIAL (required) type comment for the student + comments.append(Comment(type=CommentTypes.ESSENTIAL, params={}, comment=Comments.MALFORMED_CODE)) + finally: + if comments: + return Analysis.require(comments).dump(output_file) + + # AST - if an AST can't be made, fail and bail + try: + tree = ast.parse(user_solution) + except Exception: + # If ast.parse fails, assume malformed code and fail with an ESSENTIAL (required) type comment for the student + comments.append(Comment(type=CommentTypes.ESSENTIAL, params={}, comment=Comments.MALFORMED_CODE)) + finally: + if comments: + return Analysis.require(comments).dump(output_file) + + # Generate PyLint comments for additional feedback. + comments.extend(generate_pylint_comments(in_path)) + + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/processing-logs/analyzer.py b/lib/processing-logs/analyzer.py new file mode 100644 index 0000000..6136750 --- /dev/null +++ b/lib/processing-logs/analyzer.py @@ -0,0 +1,65 @@ +""" +Analyzer for the `cater-waiter` exercise. +""" + +import ast + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint +from pathlib import Path +from pylint.reporters.text import TextReporter + +from common import Analysis, BaseFeedback, Summary +from common.comment import Comment, CommentTypes +from common.pylint_comments import generate_pylint_comments + + +class Comments(BaseFeedback): + NO_MODULE = ("general", "no_module") + NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") + MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + + +def analyze(in_path: Path, out_path: Path): + """ + Analyze the user's Two Fer solution to give feedback. Outputs a JSON that + + conforms to https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#output-format + """ + + # List of Comment objects to process + comments = [] + + output_file = out_path.parent.joinpath("analysis.json") + + # input file - if it can't be found, fail and bail + try: + user_solution = in_path.read_text() + except OSError as err: + # fail out fast with an ESSENTIAL (required) type comment for the student + comments.append(Comment(type=CommentTypes.ESSENTIAL, params={}, comment=Comments.MALFORMED_CODE)) + finally: + if comments: + return Analysis.require(comments).dump(output_file) + + # AST - if an AST can't be made, fail and bail + try: + tree = ast.parse(user_solution) + except Exception: + # If ast.parse fails, assume malformed code and fail with an ESSENTIAL (required) type comment for the student + comments.append(Comment(type=CommentTypes.ESSENTIAL, params={}, comment=Comments.MALFORMED_CODE)) + finally: + if comments: + return Analysis.require(comments).dump(output_file) + + # Generate PyLint comments for additional feedback. + comments.extend(generate_pylint_comments(in_path)) + + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/restaurant-rozalynn/analyzer.py b/lib/restaurant-rozalynn/analyzer.py index 981e2f0..aa0e529 100644 --- a/lib/restaurant-rozalynn/analyzer.py +++ b/lib/restaurant-rozalynn/analyzer.py @@ -2,9 +2,14 @@ Analyzer for the `restaurant-rozalynn` exercise. Only Pylint checks are currently active. """ + import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -14,7 +19,10 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + def analyze(in_path: Path, out_path: Path): """ @@ -51,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/tisbury-treasure-hunt/analyzer.py b/lib/tisbury-treasure-hunt/analyzer.py index 828a595..9c29f04 100644 --- a/lib/tisbury-treasure-hunt/analyzer.py +++ b/lib/tisbury-treasure-hunt/analyzer.py @@ -2,9 +2,14 @@ Analyzer for the `tisbury-treasure-hunt` exercise. Only Pylint checks are currently active. """ + import ast -from pylint import epylint as lint + +from io import StringIO +from pylint.lint import Run +from pylint import run_pylint from pathlib import Path +from pylint.reporters.text import TextReporter from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes @@ -14,7 +19,10 @@ class Comments(BaseFeedback): NO_MODULE = ("general", "no_module") NO_METHOD = ("two-fer", "no_method") + NO_RETURN = ("general", "no_return") MALFORMED_CODE = ("general", "malformed_code") + GENERAL_RECS = ("general", "general_recommendations") + def analyze(in_path: Path, out_path: Path): """ @@ -51,4 +59,8 @@ def analyze(in_path: Path, out_path: Path): # Generate PyLint comments for additional feedback. comments.extend(generate_pylint_comments(in_path)) + # If there are no comments, add the general recommendations as comments. + if not comments: + comments.append(Comment(type=CommentTypes.INFORMATIVE, params={}, comment=Comments.GENERAL_RECS)) + return Analysis.summarize_comments(comments, output_file) diff --git a/lib/two-fer/analyzer.py b/lib/two-fer/analyzer.py index 3294f53..d082f28 100644 --- a/lib/two-fer/analyzer.py +++ b/lib/two-fer/analyzer.py @@ -2,9 +2,11 @@ Analyzer for the `two-fer` exercise. """ import ast -from pylint import epylint as lint +from io import StringIO +from pylint.lint import Run from pathlib import Path + from common import Analysis, BaseFeedback, Summary from common.comment import Comment, CommentTypes from common.pylint_comments import generate_pylint_comments diff --git a/requirements.txt b/requirements.txt index 98b36a4..d5539ec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ -pylint ==2.17.7 -plerr ~=3.0.0 +pylint ~=4.0.4 diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/acronym/acronym.py b/test/acronym/acronym.py new file mode 100644 index 0000000..5ce0e66 --- /dev/null +++ b/test/acronym/acronym.py @@ -0,0 +1,6 @@ +import re + + +def abbreviate(words): + regex = "[A-Z]+['a-z]*|['a-z]+" + return ''.join(word[0].upper() for word in re.findall(regex, words)) \ No newline at end of file diff --git a/test/acronym/analysis.json b/test/acronym/analysis.json new file mode 100644 index 0000000..a247173 --- /dev/null +++ b/test/acronym/analysis.json @@ -0,0 +1,31 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.pylint.convention", + "params": { + "lineno": "6", + "code": "C0304 missing-final-newline", + "message": "Final newline missing", + "bad_code": "Instead of: \n```python\nprint(\"Hello\") # CRLF (\\r\\n)\nprint(\"world\") # End-of-file (EOF)\n# [missing-final-newline]\n\nprint(\"Hello\") # LF (\\n)\nprint(\"world\") # End-of-file (EOF)\n# [missing-final-newline]\n\n```\n\n", + "good_code": "Try: \n```python\nprint(\"Hello\") # CRLF (\\r\\n)\nprint(\"world\") # CRLF (\\r\\n)\n# End-of-file (EOF)\n\nprint(\"Hello\") # LF (\\n)\nprint(\"world\") # LF (\\n)\n# End-of-file (EOF)\n```\n\n", + "related_info": "- [POSIX Standard](https://pubs.opengroup.org/onlinepubs/9699919799/)\n- [POSIX Standard Chapter 3.206\n Line](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206)\n", + "details": "The POSIX standard defines a line as:\n\n: \\\"A sequence of zero or more non- \\ characters plus a\n terminating \\ character.\\\"\n" + }, + "type": "informative" + }, + { + "comment": "python.pylint.convention", + "params": { + "lineno": "1", + "code": "C0114 missing-module-docstring", + "message": "Missing module docstring", + "bad_code": "Instead of: \n```python\nimport sys # [missing-module-docstring]\n\n\ndef print_python_version():\n print(sys.version)\n```\n\n", + "good_code": "Try: \n```python\n\"\"\"Module providing a function printing python version.\"\"\"\n\nimport sys\n\n\ndef print_python_version():\n print(sys.version)\n```\n\n", + "related_info": null, + "details": null + }, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/anagram/anagram.py b/test/anagram/anagram.py new file mode 100644 index 0000000..673c3d1 --- /dev/null +++ b/test/anagram/anagram.py @@ -0,0 +1,12 @@ +"""Solution code for Anagram exercise on Exercism.org.""" + + +def find_anagrams(word, candidates): + return [candidate + for candidate in candidates + if _letters(candidate) == _letters(word) + if candidate.lower() != word.lower()] + + +def _letters(word): + return sorted(word.lower()) diff --git a/test/anagram/analysis.json b/test/anagram/analysis.json new file mode 100644 index 0000000..464bec9 --- /dev/null +++ b/test/anagram/analysis.json @@ -0,0 +1,10 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.general.general_recommendations", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/black-jack/.meta/config.json b/test/black-jack/.meta/config.json new file mode 100644 index 0000000..a23fa9e --- /dev/null +++ b/test/black-jack/.meta/config.json @@ -0,0 +1,27 @@ +{ + "authors": [ + "Ticktakto", + "Yabby1997", + "limm-jk", + "OMEGA-Y", + "wnstj2007", + "pranasziaukas", + "bethanyG" + ], + "contributors": [ + "PaulT89" + ], + "files": { + "solution": [ + "black_jack.py" + ], + "test": [ + "black_jack_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "poker", + "blurb": "Learn about comparisons by implementing some Black Jack judging rules." +} diff --git a/test/black-jack/.meta/design.md b/test/black-jack/.meta/design.md new file mode 100644 index 0000000..8800837 --- /dev/null +++ b/test/black-jack/.meta/design.md @@ -0,0 +1,63 @@ +## Goal + +This concept exercise should teach how basic _non-customized_ comparisons work in python and how to use them effectively. + +## Learning objectives + +- understand all comparison operations in Python have the same priority and are evaluated after arithmetic, shifting, or bitwise operations. +- understand all comparisons yield the boolean values True and False +- know that identity comparisons is and is not are for checking an objects identity only +- understand that `==` and `!=` compare both the value & type of an object. +- know where Python has altered the behavior of `==` and `!=` for certain `built-in` types (such as [numbers][numbers], or for standard library types like [decimals][decimals], and [fractions][fractions] to allow comparison across and within type. +- know that unlike numeric types, strings (`str`) and binary sequences (`bytes` & `byte array`) **cannot** be directly compared. +- understand how comparisons work within `built-in` [sequence types][sequence types](`list`, `tuple`, `range`) and `built-in` `collection types` (`set`, `[dict]`) +- know about the "special" comparisons `None`, `NotImplemented` (comparing either should use identity operators and not equality operators because they are singleton objects) and NaN (`NaN` is **never** `==` to itself) +- use the value comparison operators `==`, `>`, `<`, `!=` with numeric types +- use the value comparison operators `==`, `>`, `<`, `!=` with non-numeric types +- use `is` and `is not` to check/verify identity + +## Out of scope + +- rich comparison with `__lt__`, `__le__`, `__ne__`, `__ge__`, `__gt__` +- understanding (_and using the concept_) that the `==` operator calls the dunder method `__eq__()` on a specific object, and uses that object's implementation for comparison. Where no implementation is present, the default `__eq__()` from generic `object` is used. +- overloading the default implementation of the `__eq__()` dunder method on a specific object to customize comparison behavior. +- `set operations` +- performance considerations + +## Concepts + +- Comparison priority in Python +- Comparison operators `==`, `>`, `<`, `!=` +- Identity methods `is` and `is not` +- Equality applied to `built-in` types +- Equivalence vs equality +- Inequality + +## Prerequisites + +- `basics` +- `booleans` +- `dicts` +- `lists` +- `sets` +- `strings` +- `tuples` +- `numbers` +- `iteration` + +## Resources + +- [Comparisons in Python (Python language reference)](https://docs.python.org/3/reference/expressions.html#comparisons) +- [Value comparisons in Python (Python language reference)](https://docs.python.org/3/reference/expressions.html#value-comparisons) +- [Identity comparisons in Python (Python language reference)](https://docs.python.org/3/reference/expressions.html#is-not) +- [Python operators official doc](https://docs.python.org/3/library/operator.html) +- [Python Object Model (Python docs)](https://docs.python.org/3/reference/datamodel.html#objects) +- [Basic Customization](https://docs.python.org/3/reference/datamodel.html#customization) +- [Python basic operators on tutorialspoint](https://www.tutorialspoint.com/python/python_basic_operators.htm) +- [Python comparison operators on data-flair](https://data-flair.training/blogs/python-comparison-operators/) +- [PEP 207 to allow Operator Overloading for Comparison](https://www.python.org/dev/peps/pep-0207/) + +[numbers]: https://docs.python.org/3/library/stdtypes.html#typesnumeric +[decimals]: https://docs.python.org/3/library/decimal.html#decimal.Decimal +[fractions]: https://docs.python.org/3/library/fractions.html#fractions.Fraction +[sequence types]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range \ No newline at end of file diff --git a/test/black-jack/.meta/exemplar.py b/test/black-jack/.meta/exemplar.py new file mode 100644 index 0000000..27c7a5d --- /dev/null +++ b/test/black-jack/.meta/exemplar.py @@ -0,0 +1,107 @@ +"""Functions to help play and score a game of blackjack. + +How to play blackjack: https://bicyclecards.com/how-to-play/blackjack/ +"Standard" playing cards: https://en.wikipedia.org/wiki/Standard_52-card_deck +""" + + +def value_of_card(card): + """Determine the scoring value of a card. + + :param card: str - given card. + :return: int - value of a given card. See below for values. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 1 + 3. '2' - '10' = numerical value. + """ + + if card in ('JQK'): + value = 10 + + elif card == 'A': + value = 1 + + else: + value = int(card) + + return value + + +def higher_card(card_one, card_two): + """Determine which card has a higher value in the hand. + + :param card_one, card_two: str - cards dealt in hand. See below for values. + :return: str or tuple - resulting Tuple contains both cards if they are of equal value. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 1 + 3. '2' - '10' = numerical value. + """ + + card_one_value = value_of_card(card_one) + card_two_value = value_of_card(card_two) + + if card_one_value == card_two_value: + result = card_one, card_two + + elif card_one_value > card_two_value: + result = card_one + + else: + result = card_two + + return result + + +def value_of_ace(card_one, card_two): + """Calculate the most advantageous value for the ace card. + + :param card_one, card_two: str - card dealt. See below for values. + :return: int - either 1 or 11 value of the upcoming ace card. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 11 (if already in hand) + 3. '2' - '10' = numerical value. + """ + + card_one_value = 11 if card_one == 'A' else value_of_card(card_one) + card_two_value = 11 if card_two == 'A' else value_of_card(card_two) + + ace_value = 1 if 11 + (card_one_value + card_two_value) > 21 else 11 + + return ace_value + + +def is_blackjack(card_one, card_two): + """Determine if the hand is a 'natural' or 'blackjack'. + + :param card_one, card_two: str - card dealt. See below for values. + :return: bool - is the hand is a blackjack (two cards worth 21). + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 11 (if already in hand) + 3. '2' - '10' = numerical value. + """ + + return (card_one == 'A' or card_two == 'A') and (value_of_card(card_one) == 10 or value_of_card(card_two) == 10) + + +def can_split_pairs(card_one, card_two): + """Determine if a player can split their hand into two hands. + + :param card_one, card_two: str - cards dealt. + :return: bool - can the hand be split into two pairs? (i.e. cards are of the same value). + """ + + return value_of_card(card_one) == value_of_card(card_two) + + +def can_double_down(card_one, card_two): + """Determine if a blackjack player can place a double down bet. + + :param card_one, card_two: str - first and second cards in hand. + :return: bool - can the hand can be doubled down? (i.e. totals 9, 10 or 11 points). + """ + + return 8 < value_of_card(card_one) + value_of_card(card_two) < 12 diff --git a/test/black-jack/analysis.json b/test/black-jack/analysis.json new file mode 100644 index 0000000..5eead60 --- /dev/null +++ b/test/black-jack/analysis.json @@ -0,0 +1,31 @@ +{ + "summary": "There are a few suggested changes that can bring your solution closer to ideal.", + "comments": [ + { + "comment": "python.pylint.convention", + "params": { + "lineno": "8", + "code": "C0116 missing-function-docstring", + "message": "Missing function or method docstring", + "bad_code": "Instead of: \n```python\nimport sys\n\n\ndef print_python_version(): # [missing-function-docstring]\n print(sys.version)\n```\n\n", + "good_code": "Try: \n```python\nimport sys\n\n\ndef print_python_version():\n \"\"\"Function printing python version.\"\"\"\n print(sys.version)\n```\n\n", + "related_info": null, + "details": null + }, + "type": "informative" + }, + { + "comment": "python.pylint.refactor", + "params": { + "lineno": "10", + "code": "R1705 no-else-return", + "message": "Unnecessary \"elif\" after \"return\", remove the leading \"el\" from \"elif", + "bad_code": "Instead of: \n```python\ndef compare_numbers(a: int, b: int) -> int:\n if a == b: # [no-else-return]\n return 0\n elif a < b:\n return -1\n else:\n return 1\n```\n\n", + "good_code": "Try: \n```python\ndef compare_numbers(a: int, b: int) -> int:\n if a == b:\n return 0\n if a < b:\n return -1\n return 1\n```\n\n", + "related_info": "- [Unnecessary-else-statements](https://www.pythonmorsels.com/unnecessary-else-statements/)\n", + "details": null + }, + "type": "actionable" + } + ] +} \ No newline at end of file diff --git a/test/black-jack/black_jack.py b/test/black-jack/black_jack.py new file mode 100644 index 0000000..01512b9 --- /dev/null +++ b/test/black-jack/black_jack.py @@ -0,0 +1,94 @@ +"""Functions to help play and score a game of blackjack. + +How to play blackjack: https://bicyclecards.com/how-to-play/blackjack/ +"Standard" playing cards: https://en.wikipedia.org/wiki/Standard_52-card_deck +""" + + +def value_of_card(card): + + if card in ('JQK'): + return 10 + + elif card == 'A': + return 1 + + else: + return int(card) + + +def higher_card(card_one, card_two): + """Determine which card has a higher value in the hand. + + :param card_one, card_two: str - cards dealt in hand. See below for values. + :return: str or tuple - resulting Tuple contains both cards if they are of equal value. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 1 + 3. '2' - '10' = numerical value. + """ + + card_one_value = value_of_card(card_one) + card_two_value = value_of_card(card_two) + + if card_one_value == card_two_value: + result = card_one, card_two + + if card_one_value > card_two_value: + result = card_one + + else: + result = card_two + + return result + + +def value_of_ace(card_one, card_two): + """Calculate the most advantageous value for the ace card. + + :param card_one, card_two: str - card dealt. See below for values. + :return: int - either 1 or 11 value of the upcoming ace card. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 11 (if already in hand) + 3. '2' - '10' = numerical value. + """ + + card_one_value = 11 if card_one == 'A' else value_of_card(card_one) + card_two_value = 11 if card_two == 'A' else value_of_card(card_two) + + return 1 if 11 + (card_one_value + card_two_value) > 21 else 11 + + +def is_blackjack(card_one, card_two): + """Determine if the hand is a 'natural' or 'blackjack'. + + :param card_one, card_two: str - card dealt. See below for values. + :return: bool - is the hand is a blackjack (two cards worth 21). + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 11 (if already in hand) + 3. '2' - '10' = numerical value. + """ + + return (card_one == 'A' or card_two == 'A') and (value_of_card(card_one) == 10 or value_of_card(card_two) == 10) + + +def can_split_pairs(card_one, card_two): + """Determine if a player can split their hand into two hands. + + :param card_one, card_two: str - cards dealt. + :return: bool - can the hand be split into two pairs? (i.e. cards are of the same value). + """ + + return value_of_card(card_one) == value_of_card(card_two) + + +def can_double_down(card_one, card_two): + """Determine if a blackjack player can place a double down bet. + + :param card_one, card_two: str - first and second cards in hand. + :return: bool - can the hand can be doubled down? (i.e. totals 9, 10 or 11 points). + """ + + return 8 < value_of_card(card_one) + value_of_card(card_two) < 12 diff --git a/test/card-games/.meta/config.json b/test/card-games/.meta/config.json new file mode 100644 index 0000000..f7d39ca --- /dev/null +++ b/test/card-games/.meta/config.json @@ -0,0 +1,24 @@ +{ + "authors": [ + "itamargal", + "isaacg", + "bethanyg" + ], + "contributors": [ + "valentin-p", + "pranasziaukas" + ], + "files": { + "solution": [ + "lists.py" + ], + "test": [ + "lists_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "poker", + "blurb": "Learn about lists by tracking hands in card games." +} diff --git a/test/card-games/.meta/design.md b/test/card-games/.meta/design.md new file mode 100644 index 0000000..91791e8 --- /dev/null +++ b/test/card-games/.meta/design.md @@ -0,0 +1,37 @@ +# Design + +## Goal + +The goal of this exercise is to teach the basics of the `list` data type in Python. The exercise will walk the student through how to create lists via various methods, iterate through lists via looping, and access items in various lists through indexing and forming/accessing list slices. + +## Learning objectives + +- Create a `list` via constructor (`list()`) & literal (`[]`) +- Combine two or more lists by concatenation via `+` +- Check for an items membership/absence in a list using `in` +- Access items in a list via index (`bracket notation`) +- Access a range of items in a list via list slicing (`[start:stop:step]`) +- Usage of `sum()` on a list with numbers +- Usage of `len()` to get the length of a list + +## Out of scope + +- List Comprehensions (these will be covered in their own concept exercise) +- List methods (`copy()`, `append()`, `sort()` etc) +- How builtin functions relate to lists (`len()`, `max()`, `min()`, `count()`, `sorted()`) +- Construction of complex or nested `lists` (i.e a `list` of `dicts`, a `list` of `lists`, a `list` of `tuples`) +- Consequences and considerations of mutability + +## Concepts + +- `lists` +- indexing +- slicing +- concatenation + +## Prerequisites + +- `basics` +- `str` +- `comparisons` +- `conditionals` diff --git a/test/card-games/.meta/exemplar.py b/test/card-games/.meta/exemplar.py new file mode 100644 index 0000000..d6531f0 --- /dev/null +++ b/test/card-games/.meta/exemplar.py @@ -0,0 +1,88 @@ +"""Functions for tracking poker hands and assorted card tasks. + +Python list documentation: https://docs.python.org/3/tutorial/datastructures.html +""" + + +def get_rounds(number): + """Create a list containing the current and next two round numbers. + + :param number: int - current round number. + :return: list - current round and the two that follow. + """ + + return [number, number + 1, number + 2] + + +def concatenate_rounds(rounds_1, rounds_2): + """Concatenate two lists of round numbers. + + :param rounds_1: list - first rounds played. + :param rounds_2: list - second set of rounds played. + :return: list - all rounds played. + """ + + return rounds_1 + rounds_2 + + +def list_contains_round(rounds, number): + """Check if the list of rounds contains the specified number. + + :param rounds: list - rounds played. + :param number: int - round number. + :return: bool - was the round played? + """ + + return number in rounds + + +def card_average(hand): + """Calculate and returns the average card value from the list. + + :param hand: list - cards in hand. + :return: float - average value of the cards in the hand. + """ + + return sum(hand) / len(hand) + + +def approx_average_is_average(hand): + """Return if the (average of first and last card values) OR ('middle' card) == calculated average. + + :param hand: list - cards in hand. + :return: bool - does one of the approximate averages equal the `true average`? + """ + + real_average = card_average(hand) + + if card_average([hand[0], hand[-1]]) == real_average: + is_same = True + elif hand[len(hand) // 2] == real_average: + is_same = True + else: + is_same = False + + return is_same + + +def average_even_is_average_odd(hand): + """Return if the (average of even indexed card values) == (average of odd indexed card values). + + :param hand: list - cards in hand. + :return: bool - are even and odd averages equal? + """ + + return card_average(hand[::2]) == card_average(hand[1::2]) + + +def maybe_double_last(hand): + """Multiply a Jack card value in the last index position by 2. + + :param hand: list - cards in hand. + :return: list - hand with Jacks (if present) value doubled. + """ + + if hand[-1] == 11: + hand[-1] *= 2 + + return hand diff --git a/test/card-games/analysis.json b/test/card-games/analysis.json new file mode 100644 index 0000000..ef42913 --- /dev/null +++ b/test/card-games/analysis.json @@ -0,0 +1,44 @@ +{ + "summary": "There are a few changes we'd like you to make before completing this exercise.", + "comments": [ + { + "comment": "python.pylint.warning", + "params": { + "lineno": "46", + "code": "W0622 redefined-builtin", + "message": "Redefining built-in 'sum'", + "bad_code": "Instead of: \n```python\ndef map(): # [redefined-builtin]\n pass\n```\n\n", + "good_code": "Try: \n```python\ndef map_iterable():\n pass\n```\n\n", + "related_info": null, + "details": "Shadowing [built-ins](https://docs.python.org/3.13/library/functions.html) at the global scope is discouraged because it\nobscures their behavior throughout the entire module, increasing the\nrisk of subtle bugs when the built-in is needed elsewhere.\n\n In contrast, local redefinitions _might_ be acceptable as their impact is confined to a\nspecific scope; although it is generally not a good idea.\n" + }, + "type": "essential" + }, + { + "comment": "python.pylint.error", + "params": { + "lineno": "46", + "code": "E0601 used-before-assignment", + "message": "Using variable 'sum' before assignment", + "bad_code": "Instead of: \n```python\nprint(hello) # [used-before-assignment]\nhello = \"Hello World !\"\n```\n\n", + "good_code": "Try: \n```python\nhello = \"Hello World !\"\nprint(hello)\n```\n\n", + "related_info": null, + "details": null + }, + "type": "essential" + }, + { + "comment": "python.pylint.refactor", + "params": { + "lineno": "62", + "code": "R1705 no-else-return", + "message": "Unnecessary \"elif\" after \"return\", remove the leading \"el\" from \"elif", + "bad_code": "Instead of: \n```python\ndef compare_numbers(a: int, b: int) -> int:\n if a == b: # [no-else-return]\n return 0\n elif a < b:\n return -1\n else:\n return 1\n```\n\n", + "good_code": "Try: \n```python\ndef compare_numbers(a: int, b: int) -> int:\n if a == b:\n return 0\n if a < b:\n return -1\n return 1\n```\n\n", + "related_info": "- [Unnecessary-else-statements](https://www.pythonmorsels.com/unnecessary-else-statements/)\n", + "details": null + }, + "type": "actionable" + } + ] +} \ No newline at end of file diff --git a/test/card-games/lists.py b/test/card-games/lists.py new file mode 100644 index 0000000..36464a0 --- /dev/null +++ b/test/card-games/lists.py @@ -0,0 +1,90 @@ +"""Functions for tracking poker hands and assorted card tasks. + +Python list documentation: https://docs.python.org/3/tutorial/datastructures.html +""" + + +def get_rounds(number): + """Create a list containing the current and next two round numbers. + + :param number: int - current round number. + :return: list - current round and the two that follow. + """ + + return [number, number + 1, number + 2] + + +def concatenate_rounds(rounds_1, rounds_2): + """Concatenate two lists of round numbers. + + :param rounds_1: list - first rounds played. + :param rounds_2: list - second set of rounds played. + :return: list - all rounds played. + """ + + return rounds_1 + rounds_2 + + +def list_contains_round(rounds, number): + """Check if the list of rounds contains the specified number. + + :param rounds: list - rounds played. + :param number: int - round number. + :return: bool - was the round played? + """ + + return bool(number in rounds) + + +def card_average(hand): + """Calculate and returns the average card value from the list. + + :param hand: list - cards in hand. + :return: float - average value of the cards in the hand. + """ + + sum = sum(hand) + size = len(hand) + average = sum/size + + return average + + +def approx_average_is_average(hand): + """Return if the (average of first and last card values) OR ('middle' card) == calculated average. + + :param hand: list - cards in hand. + :return: bool - does one of the approximate averages equal the `true average`? + """ + + real_average = card_average(hand) + + if card_average([hand[0], hand[-1]]) == real_average: + return True + elif hand[len(hand) // 2] == real_average: + return True + else: + return False + + +def average_even_is_average_odd(hand): + """Return if the (average of even indexed card values) == (average of odd indexed card values). + + :param hand: list - cards in hand. + :return: bool - are even and odd averages equal? + """ + + return card_average(hand[::2]) == card_average(hand[1::2]) + + +def maybe_double_last(hand): + """Multiply a Jack card value in the last index position by 2. + + :param hand: list - cards in hand. + :return: list - hand with Jacks (if present) value doubled. + """ + + if hand[-1] == 11: + hand[-1] *= 2 + + return hand diff --git a/test/cater-waiter/.meta/config.json b/test/cater-waiter/.meta/config.json new file mode 100644 index 0000000..89145f5 --- /dev/null +++ b/test/cater-waiter/.meta/config.json @@ -0,0 +1,25 @@ +{ + "authors": [ + "bethanyg" + ], + "contributors": [ + "zepam" + ], + "files": { + "solution": [ + "sets.py" + ], + "test": [ + "sets_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ], + "editor": [ + "sets_test_data.py", + "sets_categories_data.py" + ] + }, + "icon": "meetup", + "blurb": "Learn about sets by managing the menus and ingredients for your catering company's event." +} diff --git a/test/cater-waiter/.meta/design.md b/test/cater-waiter/.meta/design.md new file mode 100644 index 0000000..c94d703 --- /dev/null +++ b/test/cater-waiter/.meta/design.md @@ -0,0 +1,68 @@ +## Goal + +The goal of this exercise is to teach the basics of [`sets`][set type] (_set type_) in Python. + + +## Learning objectives + +* understand that a set is an **unordered collection of distinct hashable objects** +* create a `set` via constructor (`set()`) and literal (`{}`) +* de-dupe a list of elements by converting a sequence type such as a `list` to a `set` type +* check for a membership of an element in a given set via `in` +* set comparison functions and set comparison operators (`=<`, `>=`, `issubset()`, `issuperset()`, etc.) +* additional set operators (`union`, `intersection`, `difference`, and `symmetric_difference`) +* add values to a given set via `add()` +* remove values from a given set via `discard()` +* iterate through a given set by using `for item in ` and `for index, item in enumerate()` +* understand that iterating through a set twince may result in a different iteration order (_sets are unordered_) + +## Out of scope + +* `frozenset()` +* `clear()` to delete all elements of a set +* check the length of a given set via `len()` +* `remove` as opposed to `discard` (_`remove` tosses a `keyerror` if the element is not present_) +* all forms/variants of `update()` +* remove (and use) a value from a given set via `pop()` +* make shallow copy(s) of a given set via `copy()` +* using additional builtins such as `sorted()`, `min()`, or `map()` with a set +* set comprehensions + +## Concepts + +* `sets` +* [`hashable`][term-hashable] objects +* `set` comparisons +* `set` operations + +## Prerequisites + +* `basics` +* `booleans` +* `comparisons` +* `dicts` +* `lists` +* `loops` + +## Resources to refer to + +* [Set Types (Python Official Docs)][set types](https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset) +* [Hashable (Python Official Docs Glossary)][term-hashable] +* [immutable (Python Official Docs Glossary)][term-immutable] + +### Hints + +Hints should link to the `Sets` section of the Python docs tutorial: [Sets][sets-tutorial], or equivelent resources. + + +### After + +After, the student can explore comprehension syntax, although it will be taught in separate exercises. This would also be a good time to explore set comparisons via function &/or operator, or experimenting with the `issuperset()` & `issubset()` functions. + + + +[set type]: https://github.com/exercism/v3/blob/master/languages/python/reference/concepts/builtin_types/set.md +[set types]: https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset +[sets-tutorial]: https://docs.python.org/3/tutorial/datastructures.html#sets +[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable +[term-immutable]: https://docs.python.org/3/glossary.html#term-immutable \ No newline at end of file diff --git a/test/cater-waiter/.meta/exemplar.py b/test/cater-waiter/.meta/exemplar.py new file mode 100644 index 0000000..8f48c52 --- /dev/null +++ b/test/cater-waiter/.meta/exemplar.py @@ -0,0 +1,134 @@ +"""Functions for compiling dishes and ingredients for a catering company.""" + + +from sets_categories_data import (VEGAN, + VEGETARIAN, + KETO, + PALEO, + OMNIVORE, + ALCOHOLS, + SPECIAL_INGREDIENTS) + + +def clean_ingredients(dish_name, dish_ingredients): + """Remove duplicates from `dish_ingredients`. + + :param dish_name: str - containing the dish name. + :param dish_ingredients: list - dish ingredients. + :return: tuple - containing (dish_name, ingredient set). + + This function should return a `tuple` with the name of the dish as the first item, + followed by the de-duped `set` of ingredients as the second item. + """ + + return dish_name, set(dish_ingredients) + + +def check_drinks(drink_name, drink_ingredients): + """Append "Cocktail" (alcohol) or "Mocktail" (no alcohol) to `drink_name`, based on `drink_ingredients`. + + :param drink_name: str - name of the drink. + :param drink_ingredients: list - ingredients in the drink. + :return: str - drink_name appended with "Mocktail" or "Cocktail". + + The function should return the name of the drink followed by "Mocktail" (non-alcoholic) and drink + name followed by "Cocktail" (includes alcohol). + """ + + if not ALCOHOLS.isdisjoint(drink_ingredients): + return drink_name + ' Cocktail' + + return drink_name + ' Mocktail' + + +def categorize_dish(dish_name, dish_ingredients): + """Categorize `dish_name` based on `dish_ingredients`. + + :param dish_name: str - dish to be categorized. + :param dish_ingredients: list - ingredients for the dish. + :return: str - the dish name appended with ": ". + + This function should return a string with the `dish name: ` (which meal category the dish belongs to). + `` can be any one of (VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE). + All dishes will "fit" into one of the categories imported from `sets_categories_data.py` + + """ + + categories = ((VEGAN, 'VEGAN'), + (VEGETARIAN, 'VEGETARIAN'), + (KETO, 'KETO'), + (PALEO, 'PALEO'), + (OMNIVORE, 'OMNIVORE')) + + for category in categories: + if set(dish_ingredients) <= category[0]: + return dish_name + ': ' + category[1] + return None + + +def tag_special_ingredients(dish): + """Compare `dish` ingredients to `SPECIAL_INGREDIENTS`. + + :param dish: tuple - of (dish name, list of dish ingredients). + :return: tuple - containing (dish name, dish special ingredients). + + Return the dish name followed by the `set` of ingredients that require a special note on the dish description. + For the purposes of this exercise, all allergens or special ingredients that need to be tracked are in the + SPECIAL_INGREDIENTS constant imported from `sets_categories_data.py`. + """ + + return dish[0], (SPECIAL_INGREDIENTS & set(dish[1])) + + +def compile_ingredients(dishes): + """Create a master list of ingredients. + + :param dishes: list - of dish ingredient sets. + :return: set - of ingredients compiled from `dishes`. + + This function should return a `set` of all ingredients from all listed dishes. + """ + + combined_ingredients = set() + + for ingredients in dishes: + combined_ingredients = combined_ingredients.union(ingredients) + + return combined_ingredients + + +def separate_appetizers(dishes, appetizers): + """Determine which `dishes` are designated `appetizers` and remove them. + + :param dishes: list - of dish names. + :param appetizers: list - of appetizer names. + :return: list - of dish names that do not appear on appetizer list. + + The function should return the list of dish names with appetizer names removed. + Either list could contain duplicates and may require de-duping. + """ + + return list(set(dishes) - set(appetizers)) + + +def singleton_ingredients(dishes, intersection): + """Determine which `dishes` have a singleton ingredient (an ingredient that only appears once across dishes). + + :param dishes: list - of ingredient sets. + :param intersection: constant - can be one of `_INTERSECTION` constants imported from `sets_categories_data.py`. + :return: set - containing singleton ingredients. + + Each dish is represented by a `set` of its ingredients. + + Each `_INTERSECTION` is an `intersection` of all dishes in the category. `` can be any one of: + (VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE). + + The function should return a `set` of ingredients that only appear in a single dish. + """ + + all_ingredients = set() + + for ingredients in dishes: + all_ingredients = all_ingredients ^ ingredients + + return all_ingredients - intersection diff --git a/test/cater-waiter/analysis.json b/test/cater-waiter/analysis.json new file mode 100644 index 0000000..3710881 --- /dev/null +++ b/test/cater-waiter/analysis.json @@ -0,0 +1,18 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.pylint.convention", + "params": { + "lineno": "1", + "code": "C0114 missing-module-docstring", + "message": "Missing module docstring", + "bad_code": "Instead of: \n```python\nimport sys # [missing-module-docstring]\n\n\ndef print_python_version():\n print(sys.version)\n```\n\n", + "good_code": "Try: \n```python\n\"\"\"Module providing a function printing python version.\"\"\"\n\nimport sys\n\n\ndef print_python_version():\n print(sys.version)\n```\n\n", + "related_info": null, + "details": null + }, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/cater-waiter/sets.py b/test/cater-waiter/sets.py new file mode 100644 index 0000000..f718229 --- /dev/null +++ b/test/cater-waiter/sets.py @@ -0,0 +1,131 @@ +from sets_categories_data import (VEGAN, + VEGETARIAN, + KETO, + PALEO, + OMNIVORE, + ALCOHOLS, + SPECIAL_INGREDIENTS) + + +def clean_ingredients(dish_name, dish_ingredients): + """Remove duplicates from `dish_ingredients`. + + :param dish_name: str - containing the dish name. + :param dish_ingredients: list - dish ingredients. + :return: tuple - containing (dish_name, ingredient set). + + This function should return a `tuple` with the name of the dish as the first item, + followed by the de-duped `set` of ingredients as the second item. + """ + + return dish_name, set(dish_ingredients) + + +def check_drinks(drink_name, drink_ingredients): + """Append "Cocktail" (alcohol) or "Mocktail" (no alcohol) to `drink_name`, based on `drink_ingredients`. + + :param drink_name: str - name of the drink. + :param drink_ingredients: list - ingredients in the drink. + :return: str - drink_name appended with "Mocktail" or "Cocktail". + + The function should return the name of the drink followed by "Mocktail" (non-alcoholic) and drink + name followed by "Cocktail" (includes alcohol). + """ + + if not ALCOHOLS.isdisjoint(drink_ingredients): + return drink_name + ' Cocktail' + + return drink_name + ' Mocktail' + + +def categorize_dish(dish_name, dish_ingredients): + """Categorize `dish_name` based on `dish_ingredients`. + + :param dish_name: str - dish to be categorized. + :param dish_ingredients: list - ingredients for the dish. + :return: str - the dish name appended with ": ". + + This function should return a string with the `dish name: ` (which meal category the dish belongs to). + `` can be any one of (VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE). + All dishes will "fit" into one of the categories imported from `sets_categories_data.py` + + """ + + categories = ((VEGAN, 'VEGAN'), + (VEGETARIAN, 'VEGETARIAN'), + (KETO, 'KETO'), + (PALEO, 'PALEO'), + (OMNIVORE, 'OMNIVORE')) + + for category in categories: + if set(dish_ingredients) <= category[0]: + return dish_name + ': ' + category[1] + return None + + +def tag_special_ingredients(dish): + """Compare `dish` ingredients to `SPECIAL_INGREDIENTS`. + + :param dish: tuple - of (dish name, list of dish ingredients). + :return: tuple - containing (dish name, dish special ingredients). + + Return the dish name followed by the `set` of ingredients that require a special note on the dish description. + For the purposes of this exercise, all allergens or special ingredients that need to be tracked are in the + SPECIAL_INGREDIENTS constant imported from `sets_categories_data.py`. + """ + + return (dish[0], (SPECIAL_INGREDIENTS & set(dish[1]))) + + +def compile_ingredients(dishes): + """Create a master list of ingredients. + + :param dishes: list - of dish ingredient sets. + :return: set - of ingredients compiled from `dishes`. + + This function should return a `set` of all ingredients from all listed dishes. + """ + + combined_ingredients = set() + + for ingredients in dishes: + combined_ingredients = combined_ingredients.union(ingredients) + + return combined_ingredients + + +def separate_appetizers(dishes, appetizers): + """Determine which `dishes` are designated `appetizers` and remove them. + + :param dishes: list - of dish names. + :param appetizers: list - of appetizer names. + :return: list - of dish names that do not appear on appetizer list. + + The function should return the list of dish names with appetizer names removed. + Either list could contain duplicates and may require de-duping. + """ + + return list(set(dishes) - set(appetizers)) + + +def singleton_ingredients(dishes, intersection): + """Determine which `dishes` have a singleton ingredient (an ingredient that only appears once across dishes). + + :param dishes: list - of ingredient sets. + :param intersection: constant - can be one of `_INTERSECTION` constants imported from `sets_categories_data.py`. + :return: set - containing singleton ingredients. + + Each dish is represented by a `set` of its ingredients. + + Each `_INTERSECTION` is an `intersection` of all dishes in the category. `` can be any one of: + (VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE). + + The function should return a `set` of ingredients that only appear in a single dish. + """ + + all_ingredients = set() + + for ingredients in dishes: + all_ingredients = all_ingredients ^ ingredients + + return all_ingredients - intersection diff --git a/test/chaitanas-colossal-coaster/.meta/config.json b/test/chaitanas-colossal-coaster/.meta/config.json new file mode 100644 index 0000000..5374c4d --- /dev/null +++ b/test/chaitanas-colossal-coaster/.meta/config.json @@ -0,0 +1,24 @@ +{ + "authors": [ + "mohanrajanr", + "BethanyG" + ], + "contributors": [ + "BethanyG", + "valentin-p", + "pranasziaukas" + ], + "files": { + "solution": [ + "list_methods.py" + ], + "test": [ + "list_methods_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "spiral-matrix", + "blurb": "Learn useful list methods helping Chaitana manage the lines for her colossal roller coaster." +} diff --git a/test/chaitanas-colossal-coaster/.meta/design.md b/test/chaitanas-colossal-coaster/.meta/design.md new file mode 100644 index 0000000..fc4c1de --- /dev/null +++ b/test/chaitanas-colossal-coaster/.meta/design.md @@ -0,0 +1,46 @@ +# Design + +## Goal + +This concept exercise should make the student aware of a selection of `list methods` for modifying and manipulating `lists`. + +## Learning objectives + +- Understand when it is appropriate to use `list methods` +- Understand that most `list methods` mutate the original `list` and **do not** return a new or copied list. +- Use one or more of the below methods to alter a list: + - Add a single item to the end of a list with the `append()` + - Remove all items from a list with the `clear()` method + - Insert an item in a list with the `insert()` method + - Remove the item at the given index with the `pop()` method + - Remove an item from a list with the `remove()` method + - Reverse a list with the `reverse()` method + - Sort items of a list with the `sort()` method + - Return a shallow copy of a list with the `copy()` method + - Return the number of times an item occurs in a `list` with the `count()` method + - Add all items from another iterable to the end of a list with the `extend()` method + - Return the index of an item in a list with the `index()` method +- Understand which data in a `list` can be sorted and/or compared + +## Out of scope + +- Performance considerations + +## Concepts + +- iterables +- lists +- list methods + +## Prerequisites + +- basics +- booleans +- methods +- object +- iterables + +## Resources + +- [Python list methods - Python language reference](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists) +- [Python list methods on python-ds](http://www.python-ds.com/python-3-list-methods) diff --git a/test/chaitanas-colossal-coaster/.meta/exemplar.py b/test/chaitanas-colossal-coaster/.meta/exemplar.py new file mode 100644 index 0000000..24a8394 --- /dev/null +++ b/test/chaitanas-colossal-coaster/.meta/exemplar.py @@ -0,0 +1,85 @@ +"""Functions to manage and organize queues at Chaitana's roller coaster.""" + + +def add_me_to_the_queue(express_queue, normal_queue, ticket_type, person_name): + """Add a person to the 'express' or 'normal' queue depending on the ticket number. + + :param express_queue: list - names in the Fast-track queue. + :param normal_queue: list - names in the normal queue. + :param ticket_type: int - type of ticket. 1 = express, 0 = normal. + :param person_name: str - name of person to add to a queue. + :return: list - the (updated) queue the name was added to. + """ + + result = express_queue if ticket_type == 1 else normal_queue + result.append(person_name) + return result + + +def find_my_friend(queue, friend_name): + """Search the queue for a name and return their queue position (index). + + :param queue: list - names in the queue. + :param friend_name: str - name of friend to find. + :return: int - index at which the friends name was found. + """ + + return queue.index(friend_name) + + +def add_me_with_my_friends(queue, index, person_name): + """Insert the late arrival's name at a specific index of the queue. + + :param queue: list - names in the queue. + :param index: int - the index at which to add the new name. + :param person_name: str - the name to add. + :return: list - queue updated with new name. + """ + + queue.insert(index, person_name) + return queue + + +def remove_the_mean_person(queue, person_name): + """Remove the mean person from the queue by the provided name. + + :param queue: list - names in the queue. + :param person_name: str - name of mean person. + :return: list - queue update with the mean persons name removed. + """ + + queue.remove(person_name) + return queue + + +def how_many_namefellows(queue, person_name): + """Count how many times the provided name appears in the queue. + + :param queue: list - names in the queue. + :param person_name: str - name you wish to count or track. + :return: int - the number of times the name appears in the queue. + """ + + return queue.count(person_name) + + +def remove_the_last_person(queue): + """Remove the person in the last index from the queue and return their name. + + :param queue: list - names in the queue. + :return: str - name that has been removed from the end of the queue. + """ + + return queue.pop() + + +def sorted_names(queue): + """Sort the names in the queue in alphabetical order and return the result. + + :param queue: list - names in the queue. + :return: list - copy of the queue in alphabetical order. + """ + + new_queue = queue[:] + new_queue.sort() + return new_queue diff --git a/test/chaitanas-colossal-coaster/analysis.json b/test/chaitanas-colossal-coaster/analysis.json new file mode 100644 index 0000000..464bec9 --- /dev/null +++ b/test/chaitanas-colossal-coaster/analysis.json @@ -0,0 +1,10 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.general.general_recommendations", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/chaitanas-colossal-coaster/list_methods.py b/test/chaitanas-colossal-coaster/list_methods.py new file mode 100644 index 0000000..ae4da8f --- /dev/null +++ b/test/chaitanas-colossal-coaster/list_methods.py @@ -0,0 +1,85 @@ +"""Functions to manage and organize queues at Chaitana's roller coaster.""" + + +def add_me_to_the_queue(express_queue, normal_queue, ticket_type, person_name): + """Add a person to the 'express' or 'normal' queue depending on the ticket number. + + :param express_queue: list - names in the Fast-track queue. + :param normal_queue: list - names in the normal queue. + :param ticket_type: int - type of ticket. 1 = express, 0 = normal. + :param person_name: str - name of person to add to a queue. + :return: list - the (updated) queue the name was added to. + """ + + result = express_queue if ticket_type == 1 else normal_queue + result.append(person_name) + return result + + +def find_my_friend(queue, friend_name): + """Search the queue for a name and return their queue position (index). + + :param queue: list - names in the queue. + :param friend_name: str - name of friend to find. + :return: int - index at which the friends name was found. + """ + + return queue.index(friend_name) + + +def add_me_with_my_friends(queue, index, person_name): + """Insert the late arrival's name at a specific index of the queue. + + :param queue: list - names in the queue. + :param index: int - the index at which to add the new name. + :param person_name: str - the name to add. + :return: list - queue updated with new name. + """ + + queue.insert(index, person_name) + return queue + + +def remove_the_mean_person(queue, person_name): + """Remove the mean person from the queue by the provided name. + + :param queue: list - names in the queue. + :param person_name: str - name of mean person. + :return: list - queue update with the mean persons name removed. + """ + + queue.remove(person_name) + return queue + + +def how_many_namefellows(queue, person_name): + """Count how many times the provided name appears in the queue. + + :param queue: list - names in the queue. + :param person_name: str - name you wish to count or track. + :return: int - the number of times the name appears in the queue. + """ + + return queue.count(person_name) + + +def remove_the_last_person(queue): + """Remove the person in the last index from the queue and return their name. + + :param queue: list - names in the queue. + :return: str - name that has been removed from the end of the queue. + """ + + return queue.pop() + + +def sorted_names(queue): + """Sort the names in the queue in alphabetical order and return the result. + + :param queue: list - names in the queue. + :return: list - copy of the queue in alphabetical order. + """ + + new_queue = queue[:].sort() + + return new_queue diff --git a/test/currency-exchange/.meta/config.json b/test/currency-exchange/.meta/config.json new file mode 100644 index 0000000..a8188cd --- /dev/null +++ b/test/currency-exchange/.meta/config.json @@ -0,0 +1,28 @@ +{ + "authors": [ + "Ticktakto", + "Yabby1997", + "limm-jk", + "OMEGA-Y", + "wnstj2007", + "J08K" + ], + "contributors": [ + "BethanyG", + "kytrinyx", + "pranasziaukas" + ], + "files": { + "solution": [ + "exchange.py" + ], + "test": [ + "exchange_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "hyperia-forex", + "blurb": "Learn about numbers by solving Chandler's currency exchange conundrums." +} diff --git a/test/currency-exchange/analysis.json b/test/currency-exchange/analysis.json new file mode 100644 index 0000000..464bec9 --- /dev/null +++ b/test/currency-exchange/analysis.json @@ -0,0 +1,10 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.general.general_recommendations", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/currency-exchange/exchange.py b/test/currency-exchange/exchange.py new file mode 100644 index 0000000..1cd9b22 --- /dev/null +++ b/test/currency-exchange/exchange.py @@ -0,0 +1,77 @@ +"""Functions for calculating steps in exchaning currency. + +Python numbers documentation: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex + +Overview of exchanging currency when travelling: https://www.compareremit.com/money-transfer-tips/guide-to-exchanging-currency-for-overseas-travel/ +""" + +def exchange_money(budget, exchange_rate): + """ + + :param budget: float - amount of money you are planning to exchange. + :param exchange_rate: float - unit value of the foreign currency. + :return: float - exchanged value of the foreign currency you can receive. + """ + + return budget / exchange_rate + + +def get_change(budget, exchanging_value): + """ + + :param budget: float - amount of money you own. + :param exchanging_value: float - amount of your money you want to exchange now. + :return: float - amount left of your starting currency after exchanging. + """ + + return budget - exchanging_value + + +def get_value_of_bills(denomination, number_of_bills): + """ + + :param denomination: int - the value of a bill. + :param number_of_bills: int - total number of bills. + :return: int - calculated value of the bills. + """ + + return denomination * number_of_bills + + +def get_number_of_bills(amount, denomination): + """ + + :param amount: float - the total starting value. + :param denomination: int - the value of a single bill. + :return: int - number of bills that can be obtained from the amount. + """ + + return int(amount) // denomination + + +def get_leftover_of_bills(amount, denomination): + """ + + :param amount: float - the total starting value. + :param denomination: int - the value of a single bill. + :return: float - the amount that is "leftover", given the current denomination. + """ + + return amount % denomination + + +def exchangeable_value(budget, exchange_rate, spread, denomination): + """ + + :param budget: float - the amount of your money you are planning to exchange. + :param exchange_rate: float - the unit value of the foreign currency. + :param spread: int - percentage that is taken as an exchange fee. + :param denomination: int - the value of a single bill. + :return: int - maximum value you can get. + """ + + exchange_fee = (exchange_rate / 100) * spread + exchange_value = exchange_money(budget, exchange_rate + exchange_fee) + number_of_bills = get_number_of_bills(exchange_value, denomination) + value_of_bills = get_value_of_bills(denomination, number_of_bills) + return value_of_bills diff --git a/test/electric-bill/.meta/config.json b/test/electric-bill/.meta/config.json new file mode 100644 index 0000000..752ed40 --- /dev/null +++ b/test/electric-bill/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "meatball133", + "BethanyG" + ], + "contributors": ["MatthijsBlom"], + "files": { + "solution": [ + "electric_bill.py" + ], + "test": [ + "electric_bill_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "city-office", + "blurb": "Learn about numbers in Python while saving your employers money on their electric bill." +} diff --git a/test/electric-bill/.meta/design.md b/test/electric-bill/.meta/design.md new file mode 100644 index 0000000..bc4c298 --- /dev/null +++ b/test/electric-bill/.meta/design.md @@ -0,0 +1,12 @@ +# Design + +## Goal + +The goal of this exercise is to teach the student how to use arithmetic operators and type casting between `int` and `float` in Python + +## Learning objectives + +- use `+`, `-`, `*`, `/` to add, subtract, multiply, divide numbers(`int` and `float`). +- use `round()` to round values. +- use `//` to floor divide +- use `%` to calculate remainders. diff --git a/test/electric-bill/.meta/exemplar.py b/test/electric-bill/.meta/exemplar.py new file mode 100644 index 0000000..9da3d3d --- /dev/null +++ b/test/electric-bill/.meta/exemplar.py @@ -0,0 +1,51 @@ +"""Functions to help the company calculate their power usage.""" + + +def get_extra_hours(hours): + """Return the number of hours. + + :param: hours: int - number of hours. + :return: int - number of "extra" hours. + """ + + return (hours + 3) % 24 + + +def get_kW_amount(watts): + """Return the kW amount of a given watt amount. + + :param: watts: int - watt amount. + :return: float - kW amount. + """ + + # rounds to one decimal place here + return round(watts / 1000, 1) + + +def get_kwh_amount(watts): + """Return the kWh amount of a given watt amount and hours. + + :param: watts: int - watt amount. + :return: int - kilowatt hour amount. + """ + return get_kW_amount(watts) // 3600 + + +def get_efficiency(power_factor): + """Return the efficiency calculated from the power factor. + + :param: power_factor: float. + :return: float - efficiency. + """ + return power_factor / 100 + + +def get_cost(watts, power_factor, price): + """Calculate the cost of a given kWh value, efficiency and price. + + :param: watts: int - watt value. + :param: power_factor: float - efficiency. + :param: price: float - price of kWh. + :return: float - cost of kWh. + """ + return price * (get_kwh_amount(watts) / get_efficiency(power_factor)) diff --git a/test/electric-bill/analysis.json b/test/electric-bill/analysis.json new file mode 100644 index 0000000..3faa446 --- /dev/null +++ b/test/electric-bill/analysis.json @@ -0,0 +1,18 @@ +{ + "summary": "There are a few suggested changes that can bring your solution closer to ideal.", + "comments": [ + { + "comment": "python.pylint.convention", + "params": { + "lineno": "14", + "code": "C0103 invalid-name", + "message": "Function name \"get_kW_amount\" doesn't conform to snake_case naming style ('([^\\\\W\\\\dA-Z][^\\\\WA-Z]*|_[^\\\\WA-Z]*|__[^\\\\WA-Z\\\\d_][^\\\\WA-Z]+__)$' pattern)", + "bad_code": "Instead of: \n```python\nclass cat: # [invalid-name]\n def Meow(self, NUMBER_OF_MEOW): # [invalid-name, invalid-name]\n print(\"Meow\" * NUMBER_OF_MEOW)\n return NUMBER_OF_MEOW\n\n\nCat = cat().Meow(42) # [invalid-name]\n```\n\n", + "good_code": "Try: \n```python\nclass Cat:\n def meow(self, number_of_meow):\n print(\"Meow\" * number_of_meow)\n return number_of_meow\n\n\nCAT = Cat().meow(42)\n```\n\n", + "related_info": null, + "details": "By default, Pylint will enforce\n[PEP8](https://peps.python.org/pep-0008)-suggested names.\n\nThe following naming suggestions are used for Exercism exercises:\n\n- modules (code files): snake_case\n- constants: UPPER_CASE\n- variables: snake_case (minimum of 3 letters)\n- functions: snake_case\n- arguments: snake_case\n- attributes: snake_case\n- classes: PascalCase\n- class attributes: any (no specific format)\n- class constants: UPPER_CASE\n- class methods: snake_case\n- inline variables and loop variables: snake_case\n" + }, + "type": "actionable" + } + ] +} \ No newline at end of file diff --git a/test/electric-bill/electric_bill.py b/test/electric-bill/electric_bill.py new file mode 100644 index 0000000..9da3d3d --- /dev/null +++ b/test/electric-bill/electric_bill.py @@ -0,0 +1,51 @@ +"""Functions to help the company calculate their power usage.""" + + +def get_extra_hours(hours): + """Return the number of hours. + + :param: hours: int - number of hours. + :return: int - number of "extra" hours. + """ + + return (hours + 3) % 24 + + +def get_kW_amount(watts): + """Return the kW amount of a given watt amount. + + :param: watts: int - watt amount. + :return: float - kW amount. + """ + + # rounds to one decimal place here + return round(watts / 1000, 1) + + +def get_kwh_amount(watts): + """Return the kWh amount of a given watt amount and hours. + + :param: watts: int - watt amount. + :return: int - kilowatt hour amount. + """ + return get_kW_amount(watts) // 3600 + + +def get_efficiency(power_factor): + """Return the efficiency calculated from the power factor. + + :param: power_factor: float. + :return: float - efficiency. + """ + return power_factor / 100 + + +def get_cost(watts, power_factor, price): + """Calculate the cost of a given kWh value, efficiency and price. + + :param: watts: int - watt value. + :param: power_factor: float - efficiency. + :param: price: float - price of kWh. + :return: float - cost of kWh. + """ + return price * (get_kwh_amount(watts) / get_efficiency(power_factor)) diff --git a/test/ellens-alien-game/.meta/config.json b/test/ellens-alien-game/.meta/config.json new file mode 100644 index 0000000..111ebdc --- /dev/null +++ b/test/ellens-alien-game/.meta/config.json @@ -0,0 +1,24 @@ +{ + "authors": [ + "PaulT89", + "BethanyG" + ], + "contributors": [ + "DjangoFett", + "kotp", + "IsaacG" + ], + "files": { + "solution": [ + "classes.py" + ], + "test": [ + "classes_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "character-study", + "blurb": "Learn about classes by creating an Alien for Ellen's game." +} diff --git a/test/ellens-alien-game/.meta/design.md b/test/ellens-alien-game/.meta/design.md new file mode 100644 index 0000000..4d7be13 --- /dev/null +++ b/test/ellens-alien-game/.meta/design.md @@ -0,0 +1,64 @@ +# Design + +## Goal + +The goal of this exercise is to introduce the student to the concept of classes. + +## Learning objectives + +- Understand the _basic_ idea behind Object Oriented Programming (OOP). +- Learn what a class represents. +- Learn what an object is. +- Understand the difference between classes and objects. +- Know how to create a class. +- Know how to create objects. +- Understand that instantiating a class creates an object. +- Know that `__init__()` is a 'constructor' and is used to initialize the object upon instantiation. +- Know that `__init__()` is called upon instantiation/object creation. +- Know what a method is and how it differs from a function. +- Know how to create a method. +- Implement instance variables. +- Implement class variables. +- Understand the difference between instance variables and class variables. +- Use `pass` as a placeholder for class methods. + +## Out of scope + +- `class-customiation`, including `@classmethod` & `@staticmethod` - but ok to mention in `about.md`. +- `@property` decorator, getters, and setters +- class members & non-public methods +- `class-inheritance`, `multiple-inheritance`, `__super()__`, class mix-ins +- `class-composition` +- `dataclasses` +- performance considerations + +## Concepts + +- `attributes` +- `classes` +- `methods` +- `objects` +- `OOP` + +## Prerequisites + +These are the concepts/concept exercises the student needs to complete/understand before solving this concept exercise. + +- `basics` +- `bools` +- `comparisons` +- `dicts` +- `iteration` +- `lists` +- `numbers` +- `sequences` +- `sets` +- `strings` +- `tuples` + +## Resources + +- [Classes (Python tutorial)](https://docs.python.org/3/tutorial/classes.html) +- [Python Data Model - Python Docs](https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy) +- [Real Python: Object-Oriented Programming in Python 3](https://realpython.com/python3-object-oriented-programming/) +- [DigitalOcean: How To Construct Classes & Objects in Python 3](https://www.digitalocean.com/community/tutorials/how-to-construct-classes-and-define-objects-in-python-3) diff --git a/test/ellens-alien-game/.meta/exemplar.py b/test/ellens-alien-game/.meta/exemplar.py new file mode 100644 index 0000000..ace3164 --- /dev/null +++ b/test/ellens-alien-game/.meta/exemplar.py @@ -0,0 +1,90 @@ +"""Exemplar solution to Ellen's Alien Game exercise.""" + + +class Alien: + """Create an Alien object with location x_coordinate and y_coordinate. + + Attributes + ---------- + (class)total_aliens_created: int + x_coordinate: int - Position on the x-axis. + y_coordinate: int - Position on the y-axis. + health: int - Number of health points. + + Methods + ------- + hit(): Decrement Alien health by one point. + is_alive(): Return a boolean to indicate if Alien is alive (if health is > 0). + teleport(new_x_coordinate, new_y_coordinate): Move Alien object to new coordinates. + collision_detection(other): Implementation TBD. + """ + + total_aliens_created = 0 + + def __init__(self, x_coordinate, y_coordinate): + """Initialize a new Alien object and increment total_aliens_created by 1. + + :param x_coordinate: int - Alien position on the x-axis + :param y_coordinate: int - Alien position on the y-axis + + :attribute x_coordinate: int - Alien position on the x-axis + :attribute y_coordinate: int - Alien position on the y-axis + :attribute health: int (3) - Initial Alien health points. + + :return: object - New Alien. + """ + + Alien.total_aliens_created += 1 + + self.x_coordinate = x_coordinate + self.y_coordinate = y_coordinate + self.health = 3 + + def hit(self): + """Decrement Alien health by 1. + + :return: None + """ + + #There are two valid interpretations for this method/task. + #The one below, and `self.health = max(0, self.health - 1)` + #The tests for this task reflect this ambiguity. + self.health -= 1 + + def is_alive(self): + """Return if the Alien is alive. + + :return: boolean + """ + + return self.health > 0 + + def teleport(self, new_x_coordinate, new_y_coordinate): + """Change Alien location. + + :param new_x_coordinate: int - New location on x-axis. + :param new_y_coordinate: int - New location on y-axis. + + :return: None + """ + self.x_coordinate = new_x_coordinate + self.y_coordinate = new_y_coordinate + + def collision_detection(self, other): + """Detect collisions with another Alien. + + :param other: object - Other Alien object. + + :return: None + """ + + pass + +def new_aliens_collection(positions): + """Create a list of Alien instances from a list of coordinate tuples. + + :param positions: list - List of tuples of (x, y) coordinates. + + :return: list - List of Alien objects. + """ + return [Alien(position[0], position[1]) for position in positions] diff --git a/test/ellens-alien-game/analysis.json b/test/ellens-alien-game/analysis.json new file mode 100644 index 0000000..464bec9 --- /dev/null +++ b/test/ellens-alien-game/analysis.json @@ -0,0 +1,10 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.general.general_recommendations", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/ellens-alien-game/classes.py b/test/ellens-alien-game/classes.py new file mode 100644 index 0000000..ace3164 --- /dev/null +++ b/test/ellens-alien-game/classes.py @@ -0,0 +1,90 @@ +"""Exemplar solution to Ellen's Alien Game exercise.""" + + +class Alien: + """Create an Alien object with location x_coordinate and y_coordinate. + + Attributes + ---------- + (class)total_aliens_created: int + x_coordinate: int - Position on the x-axis. + y_coordinate: int - Position on the y-axis. + health: int - Number of health points. + + Methods + ------- + hit(): Decrement Alien health by one point. + is_alive(): Return a boolean to indicate if Alien is alive (if health is > 0). + teleport(new_x_coordinate, new_y_coordinate): Move Alien object to new coordinates. + collision_detection(other): Implementation TBD. + """ + + total_aliens_created = 0 + + def __init__(self, x_coordinate, y_coordinate): + """Initialize a new Alien object and increment total_aliens_created by 1. + + :param x_coordinate: int - Alien position on the x-axis + :param y_coordinate: int - Alien position on the y-axis + + :attribute x_coordinate: int - Alien position on the x-axis + :attribute y_coordinate: int - Alien position on the y-axis + :attribute health: int (3) - Initial Alien health points. + + :return: object - New Alien. + """ + + Alien.total_aliens_created += 1 + + self.x_coordinate = x_coordinate + self.y_coordinate = y_coordinate + self.health = 3 + + def hit(self): + """Decrement Alien health by 1. + + :return: None + """ + + #There are two valid interpretations for this method/task. + #The one below, and `self.health = max(0, self.health - 1)` + #The tests for this task reflect this ambiguity. + self.health -= 1 + + def is_alive(self): + """Return if the Alien is alive. + + :return: boolean + """ + + return self.health > 0 + + def teleport(self, new_x_coordinate, new_y_coordinate): + """Change Alien location. + + :param new_x_coordinate: int - New location on x-axis. + :param new_y_coordinate: int - New location on y-axis. + + :return: None + """ + self.x_coordinate = new_x_coordinate + self.y_coordinate = new_y_coordinate + + def collision_detection(self, other): + """Detect collisions with another Alien. + + :param other: object - Other Alien object. + + :return: None + """ + + pass + +def new_aliens_collection(positions): + """Create a list of Alien instances from a list of coordinate tuples. + + :param positions: list - List of tuples of (x, y) coordinates. + + :return: list - List of Alien objects. + """ + return [Alien(position[0], position[1]) for position in positions] diff --git a/test/ghost-gobble-arcade-game/.meta/config.json b/test/ghost-gobble-arcade-game/.meta/config.json new file mode 100644 index 0000000..9065b64 --- /dev/null +++ b/test/ghost-gobble-arcade-game/.meta/config.json @@ -0,0 +1,26 @@ +{ + "authors": [ + "neenjaw" + ], + "contributors": [ + "cmccandless", + "BethanyG" + ], + "files": { + "solution": [ + "arcade_game.py" + ], + "test": [ + "arcade_game_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "language_versions": ">=3.5", + "forked_from": [ + "elixir/pacman-rules" + ], + "icon": "matching-brackets", + "blurb": "Learn about bools by setting up the rules for the Ghost Gobble arcade game." +} diff --git a/test/ghost-gobble-arcade-game/.meta/design.md b/test/ghost-gobble-arcade-game/.meta/design.md new file mode 100644 index 0000000..ac4a5a6 --- /dev/null +++ b/test/ghost-gobble-arcade-game/.meta/design.md @@ -0,0 +1,49 @@ +# Design + +## Goal + +The goal of this exercise is to teach the student how [`bool`][bools] is implemented and used in Python. We will teach the `bool` type by showing the student how to use it. + +## Learning objectives + +- create a `bool` object via literal declaration +- obtain a `bool` object from using [logical operators][logical operators] (`and`, `or`, `not`) + +## Out of scope + +- obtain a `bool` object from [comparison operators][comparison operators] (`>`, `<`, `==`) +- obtain a `bool` object from [membership operators][membership operators] (`in`, `not in`) +- obtain a `bool` object from [identity operators][identity operators] (`is`, `is not`) +- 'truthyness' and 'falsyness' + - create a `bool` object from other objects (`collections`, `integers`, `strings`) with the `bool()` constructor. +- catching/raising Exceptions +- covering that `bool` is a subtype of `int` +- bitwise logical operators +- orders of evaluation + +## Concepts + +- **booleans:** know of the existence of the bool type and its two values; know about boolean operators and how to build logical expressions with them; know of the boolean operator precedence rules. + +## Prerequisites + +- `basics` +- `comparison-operators` + +## Resources + +- [Boolean Operations — and, or, not][Boolean Operations — and, or, not] +- [Built-in Functions - bool][Built-in Functions - bool] +- [Truth Value Testing][Truth Value Testing] +- [Comparisons][Comparisons] + + +[Boolean Operations — and, or, not]: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not +[Built-in Functions - bool]: https://docs.python.org/3/library/functions.html#bool +[Comparisons]: https://docs.python.org/3/library/stdtypes.html#comparisons +[Truth Value Testing]: https://docs.python.org/3/library/stdtypes.html#truth +[bools]: https://github.com/exercism/v3/blob/master/languages/python/reference/concepts/builtin_types/bool.md +[comparison operators]: https://docs.python.org/3/reference/expressions.html#value-comparisons +[identity operators]: https://docs.python.org/3/reference/expressions.html#is-not +[logical operators]: https://docs.python.org/3/reference/expressions.html#boolean-operations +[membership operators]: https://docs.python.org/3/reference/expressions.html#membership-test-operations diff --git a/test/ghost-gobble-arcade-game/.meta/exemplar.py b/test/ghost-gobble-arcade-game/.meta/exemplar.py new file mode 100644 index 0000000..4de10a2 --- /dev/null +++ b/test/ghost-gobble-arcade-game/.meta/exemplar.py @@ -0,0 +1,46 @@ +"""Functions for implementing the rules of the classic arcade game Pac-Man.""" + + +def eat_ghost(power_pellet_active, touching_ghost): + """Verify that Pac-Man can eat a ghost if he is empowered by a power pellet. + + :param power_pellet_active: bool - does the player have an active power pellet? + :param touching_ghost: bool - is the player touching a ghost? + :return: bool - can a ghost be eaten? + """ + + return power_pellet_active and touching_ghost + + +def score(touching_power_pellet, touching_dot): + """Verify that Pac-Man has scored when a power pellet or dot has been eaten. + + :param touching_power_pellet: bool - is the player touching a power pellet? + :param touching_dot: bool - is the player touching a dot? + :return: bool - has the player scored or not? + """ + + return touching_power_pellet or touching_dot + + +def lose(power_pellet_active, touching_ghost): + """Trigger the game loop to end (GAME OVER) when Pac-Man touches a ghost without his power pellet. + + :param power_pellet_active: bool - does the player have an active power pellet? + :param touching_ghost: bool - is the player touching a ghost? + :return: bool - has the player lost the game? + """ + + return not power_pellet_active and touching_ghost + + +def win(has_eaten_all_dots, power_pellet_active, touching_ghost): + """Trigger the victory event when all dots have been eaten. + + :param has_eaten_all_dots: bool - has the player "eaten" all the dots? + :param power_pellet_active: bool - does the player have an active power pellet? + :param touching_ghost: bool - is the player touching a ghost? + :return: bool - has the player won the game? + """ + + return has_eaten_all_dots and not lose(power_pellet_active, touching_ghost) diff --git a/test/ghost-gobble-arcade-game/analysis.json b/test/ghost-gobble-arcade-game/analysis.json new file mode 100644 index 0000000..464bec9 --- /dev/null +++ b/test/ghost-gobble-arcade-game/analysis.json @@ -0,0 +1,10 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.general.general_recommendations", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/ghost-gobble-arcade-game/arcade_game.py b/test/ghost-gobble-arcade-game/arcade_game.py new file mode 100644 index 0000000..0ff5be6 --- /dev/null +++ b/test/ghost-gobble-arcade-game/arcade_game.py @@ -0,0 +1,60 @@ +"""Functions for implementing the rules of the classic arcade game Pac-Man. + +Note for future Analyzer for this exercise: consider a custom rule to have the boolean expression done +directly in the return statement, rather than using if statements. +""" + + +def eat_ghost(power_pellet_active, touching_ghost): + """Verify that Pac-Man can eat a ghost if he is empowered by a power pellet. + + :param power_pellet_active: bool - does the player have an active power pellet? + :param touching_ghost: bool - is the player touching a ghost? + :return: bool - can a ghost be eaten? + """ + + if power_pellet_active: + if touching_ghost: + return True + + return False + + +def score(touching_power_pellet, touching_dot): + """Verify that Pac-Man has scored when a power pellet or dot has been eaten. + + :param touching_power_pellet: bool - is the player touching a power pellet? + :param touching_dot: bool - is the player touching a dot? + :return: bool - has the player scored or not? + """ + + if touching_power_pellet: + return True + if touching_dot: + return True + + return False + + +def lose(power_pellet_active, touching_ghost): + """Trigger the game loop to end (GAME OVER) when Pac-Man touches a ghost without his power pellet. + + :param power_pellet_active: bool - does the player have an active power pellet? + :param touching_ghost: bool - is the player touching a ghost? + :return: bool - has the player lost the game? + """ + + return not power_pellet_active and touching_ghost + + +def win(has_eaten_all_dots, power_pellet_active, touching_ghost): + """Trigger the victory event when all dots have been eaten. + + :param has_eaten_all_dots: bool - has the player "eaten" all the dots? + :param power_pellet_active: bool - does the player have an active power pellet? + :param touching_ghost: bool - is the player touching a ghost? + :return: bool - has the player won the game? + """ + + + return has_eaten_all_dots and not lose(power_pellet_active, touching_ghost) diff --git a/test/guidos-gorgeous-lasagna/.meta/config.json b/test/guidos-gorgeous-lasagna/.meta/config.json new file mode 100644 index 0000000..f6de0d5 --- /dev/null +++ b/test/guidos-gorgeous-lasagna/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "BethanyG" + ], + "files": { + "solution": [ + "lasagna.py" + ], + "test": [ + "lasagna_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "forked_from": [ + "csharp/lucians-luscious-lasagna", + "ruby/lasagna" + ], + "icon": "lasagna", + "blurb": "Learn the basics of Python by cooking Guido's Gorgeous Lasagna." +} diff --git a/test/guidos-gorgeous-lasagna/.meta/design.md b/test/guidos-gorgeous-lasagna/.meta/design.md new file mode 100644 index 0000000..b3e64dd --- /dev/null +++ b/test/guidos-gorgeous-lasagna/.meta/design.md @@ -0,0 +1,54 @@ +# Design + +## Goal + +The goal of this exercise is to teach the student the basics of programming in Python. + +## Learning objectives + +- Know what a variable (_name_) is. +- Know how to name a variable. +- Know how to bind and re-bind a _name_ to a value (_assign or re-assign a value to a variable or assign a value to a constant_). +- Know what a `function` is and how to define one via `def`. +- Know how to add one or more `parameters` to a function definition. +- Know what the `return` keyword is, and how to use it to return a value from a function. +- Know how to call a function with zero or more arguments. +- Know how to declare an integer and/or float. +- Know how to use mathematical operators `+` and `*` on integers and/or floats. +- Know how to define single- and multi-line comments. +- Know how to define docstrings. + +## Out of scope + +- built-in types +- built-in data structures +- Classes +- Lambdas +- Methods and other function usages (_functions bound to classes, functions as arguments, nested functions_). +- Memory and performance characteristics. +- Default function parameters. +- Variadic function parameters. (_\*args and \*\*kwargs_) + +## Concepts + +`basics`: + +- function definition & `return` keyword +- integers & floats, +- mathematical operators `+` and `*` +- single and muliti-line comments. +- updating variable values +- using functions +- variables, variable assignment, & variable naming + +## Prerequisites + +There are no prerequisites for this exercise. + +## Representer + +This exercise does not require any specific representation logic to be added to the representer. + +## Analyzer + +This exercise does not require any specific representation logic to be added to the analyzer. diff --git a/test/guidos-gorgeous-lasagna/.meta/exemplar.py b/test/guidos-gorgeous-lasagna/.meta/exemplar.py new file mode 100644 index 0000000..ff0bff3 --- /dev/null +++ b/test/guidos-gorgeous-lasagna/.meta/exemplar.py @@ -0,0 +1,50 @@ +"""Functions used in preparing Guido's gorgeous lasagna. + +Learn about Guido, the creator of the Python language: https://en.wikipedia.org/wiki/Guido_van_Rossum +""" + +# time the lasagna should be in the oven according to the cookbook. +EXPECTED_BAKE_TIME = 40 +PREPARATION_TIME = 2 + + +def bake_time_remaining(elapsed_bake_time): + """Calculate the bake time remaining. + + :param elapsed_bake_time: int - baking time already elapsed. + :return: int - remaining bake time (in minutes) derived from 'EXPECTED_BAKE_TIME'. + + Function that takes the actual minutes the lasagna has been in the oven as + an argument and returns how many minutes the lasagna still needs to bake + based on the `EXPECTED_BAKE_TIME`. + """ + + return EXPECTED_BAKE_TIME - elapsed_bake_time + + +def preparation_time_in_minutes(number_of_layers): + """Calculate the preparation time. + + :param number_of_layers: int - the number of lasagna layers made. + :return: int - amount of prep time (in minutes), based on 2 minutes per layer added. + + This function takes an integer representing the number of layers added to the dish, + calculating preparation time using a time of 2 minutes per layer added. + """ + + return number_of_layers * PREPARATION_TIME + + +def elapsed_time_in_minutes(number_of_layers, elapsed_bake_time): + """Calculate the elapsed time. + + :param number_of_layers: int - the number of layers in the lasagna. + :param elapsed_bake_time: int - elapsed cooking time. + :return: int - total time elapsed (in in minutes) preparing and cooking. + + This function takes two integers representing the number of lasagna layers and the + time already spent baking and calculates the total elapsed minutes spent cooking the + lasagna. + """ + + return preparation_time_in_minutes(number_of_layers) + elapsed_bake_time diff --git a/test/guidos-gorgeous-lasagna/analysis.json b/test/guidos-gorgeous-lasagna/analysis.json new file mode 100644 index 0000000..86eaacb --- /dev/null +++ b/test/guidos-gorgeous-lasagna/analysis.json @@ -0,0 +1,18 @@ +{ + "summary": "There are a few suggested changes that can bring your solution closer to ideal.", + "comments": [ + { + "comment": "python.pylint.convention", + "params": { + "lineno": "7", + "code": "C0103 invalid-name", + "message": "Constant name \"expected_bake_time\" doesn't conform to UPPER_CASE naming style ('([^\\\\W\\\\da-z][^\\\\Wa-z]*|__.*__)$' pattern)", + "bad_code": "Instead of: \n```python\nclass cat: # [invalid-name]\n def Meow(self, NUMBER_OF_MEOW): # [invalid-name, invalid-name]\n print(\"Meow\" * NUMBER_OF_MEOW)\n return NUMBER_OF_MEOW\n\n\nCat = cat().Meow(42) # [invalid-name]\n```\n\n", + "good_code": "Try: \n```python\nclass Cat:\n def meow(self, number_of_meow):\n print(\"Meow\" * number_of_meow)\n return number_of_meow\n\n\nCAT = Cat().meow(42)\n```\n\n", + "related_info": null, + "details": "By default, Pylint will enforce\n[PEP8](https://peps.python.org/pep-0008)-suggested names.\n\nThe following naming suggestions are used for Exercism exercises:\n\n- modules (code files): snake_case\n- constants: UPPER_CASE\n- variables: snake_case (minimum of 3 letters)\n- functions: snake_case\n- arguments: snake_case\n- attributes: snake_case\n- classes: PascalCase\n- class attributes: any (no specific format)\n- class constants: UPPER_CASE\n- class methods: snake_case\n- inline variables and loop variables: snake_case\n" + }, + "type": "actionable" + } + ] +} \ No newline at end of file diff --git a/test/guidos-gorgeous-lasagna/lasagna.py b/test/guidos-gorgeous-lasagna/lasagna.py new file mode 100644 index 0000000..5376ce6 --- /dev/null +++ b/test/guidos-gorgeous-lasagna/lasagna.py @@ -0,0 +1,50 @@ +"""Functions used in preparing Guido's gorgeous lasagna. + +Learn about Guido, the creator of the Python language: https://en.wikipedia.org/wiki/Guido_van_Rossum +""" + +# time the lasagna should be in the oven according to the cookbook. +expected_bake_time = 40 +PREPARATION_TIME = 2 + + +def bake_time_remaining(elapsed_bake_time): + """Calculate the bake time remaining. + + :param elapsed_bake_time: int - baking time already elapsed. + :return: int - remaining bake time (in minutes) derived from 'EXPECTED_BAKE_TIME'. + + Function that takes the actual minutes the lasagna has been in the oven as + an argument and returns how many minutes the lasagna still needs to bake + based on the `EXPECTED_BAKE_TIME`. + """ + + return expected_bake_time - elapsed_bake_time + + +def preparation_time_in_minutes(number_of_layers): + """Calculate the preparation time. + + :param number_of_layers: int - the number of lasagna layers made. + :return: int - amount of prep time (in minutes), based on 2 minutes per layer added. + + This function takes an integer representing the number of layers added to the dish, + calculating preparation time using a time of 2 minutes per layer added. + """ + + return number_of_layers * PREPARATION_TIME + + +def elapsed_time_in_minutes(number_of_layers, elapsed_bake_time): + """Calculate the elapsed time. + + :param number_of_layers: int - the number of layers in the lasagna. + :param elapsed_bake_time: int - elapsed cooking time. + :return: int - total time elapsed (in in minutes) preparing and cooking. + + This function takes two integers representing the number of lasagna layers and the + time already spent baking and calculates the total elapsed minutes spent cooking the + lasagna. + """ + + return preparation_time_in_minutes(number_of_layers) + elapsed_bake_time diff --git a/test/inventory-management/.meta/config.json b/test/inventory-management/.meta/config.json new file mode 100644 index 0000000..c08624a --- /dev/null +++ b/test/inventory-management/.meta/config.json @@ -0,0 +1,24 @@ +{ + "authors": [ + "j08k" + ], + "contributors": [ + "valentin-p", + "bethanyG", + "mukeshgurpude", + "kotp" + ], + "files": { + "solution": [ + "dicts.py" + ], + "test": [ + "dicts_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "high-scores", + "blurb": "Learn about dicts by managing warehouse inventory." +} diff --git a/test/inventory-management/.meta/design.md b/test/inventory-management/.meta/design.md new file mode 100644 index 0000000..916187a --- /dev/null +++ b/test/inventory-management/.meta/design.md @@ -0,0 +1,60 @@ +# Design + +## Goal + +The goal of this exercise is to teach the basics of the `dict` (dictionary, mapping) data type in Python. + +## Learning objectives + +- create a `dict` via constructor and literal. +- access values in a `dict` via `keys`. +- assign values in a `dict` via key reference. +- check for membership of a key in a given dictionary. +- add new `key`:`value` pairs to the `dict`. +- remove `key`:`value` pairs from the `dict`. +- remove `key`:`value` pairs from the `dict` using `dict.pop()`. +- iterate through a `dict` using `dict.keys()`, `dict.values()`, or `dict.items()`. +- `dict` method `setdefault()` + +## Out of scope + +- Dictionary comprehensions +- `dict` methods such as `get()` or `clear()`. +- Built-in functions as they relate to this data structure (_e.g._ `copy()`, `len()`, or `enumerate()`. +- Sorting by `keys` or `values`. +- Swapping `keys` for `values`. +- Knowing that Dictionaries can be _nested_, _-- e.g._ ' a dictionary of dictionaries'. +- Mutability +- `copy()` vs `deepcopy()` +- Related `collections` module with `Counter()` and `defaultdict()` +- Memory and performance characteristics. + +## Concepts + +- `dicts` + +## Prerequisites + +- `booleans` +- `for-loops` +- `functions` +- `if-keyword` +- `in-keyword` +- `integers` +- `return-keyword` +- `strings` +- `tuples` +- `lists` + +## Representer + +This exercise does not require any logic to be added to the [representer][representer] + +## Analyzer + +This exercise does not require any logic to be added to the [analyzer][analyzer]. + +[builtin-types]: https://github.com/exercism/v3/tree/master/languages/python/reference/concepts/builtin_types +[data-types]: https://github.com/exercism/v3/blob/master/languages/python/reference/concepts/data_structures.md +[analyzer]: https://github.com/exercism/python-analyzer +[representer]: https://github.com/exercism/python-representer diff --git a/test/inventory-management/.meta/exemplar.py b/test/inventory-management/.meta/exemplar.py new file mode 100644 index 0000000..ac02bad --- /dev/null +++ b/test/inventory-management/.meta/exemplar.py @@ -0,0 +1,68 @@ +"""Functions to keep track and alter inventory.""" + + +def create_inventory(items): + """Create a dict that tracks the amount (count) of each element on the `items` list. + + :param items: list - list of items to create an inventory from. + :return: dict - the inventory dictionary. + """ + + inventory = {} + add_items(inventory, items) + return inventory + + +def add_items(inventory, items): + """Add or increment items in inventory using elements from the items `list`. + + :param inventory: dict - dictionary of existing inventory. + :param items: list - list of items to update the inventory with. + :return: dict - the inventory updated with the new items. + """ + + for item in items: + inventory.setdefault(item, 0) + inventory[item] += 1 + return inventory + + +def decrement_items(inventory, items): + """Decrement items in inventory using elements from the `items` list. + + :param inventory: dict - inventory dictionary. + :param items: list - list of items to decrement from the inventory. + :return: dict - updated inventory with items decremented. + """ + + for item in items: + if item in inventory: + inventory[item] = max(inventory[item] - 1, 0) + return inventory + + +def remove_item(inventory, item): + """Remove item from inventory if it matches `item` string. + + :param inventory: dict - inventory dictionary. + :param item: str - item to remove from the inventory. + :return: dict - updated inventory with item removed. Current inventory if item does not match. + """ + + if item in inventory: + inventory.pop(item) + return inventory + + +def list_inventory(inventory): + """Create a list containing only available (item_name, item_count > 0) pairs in inventory. + + :param inventory: dict - an inventory dictionary. + :return: list of tuples - list of key, value pairs from the inventory dictionary. + """ + + output = [] + for item in sorted(inventory.items()): + if item[1] > 0: + output.append(item) + return output diff --git a/test/inventory-management/analysis.json b/test/inventory-management/analysis.json new file mode 100644 index 0000000..464bec9 --- /dev/null +++ b/test/inventory-management/analysis.json @@ -0,0 +1,10 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.general.general_recommendations", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/inventory-management/dicts.py b/test/inventory-management/dicts.py new file mode 100644 index 0000000..66c58cf --- /dev/null +++ b/test/inventory-management/dicts.py @@ -0,0 +1,74 @@ +"""Functions to keep track and alter inventory. + +Consider writing a custom analyzer/checker to suggest comprehensions. +""" + + +def create_inventory(items): + """Create a dict that tracks the amount (count) of each element on the `items` list. + + :param items: list - list of items to create an inventory from. + :return: dict - the inventory dictionary. + """ + + inventory = {} + add_items(inventory, items) + return inventory + + +def add_items(inventory, items): + """Add or increment items in inventory using elements from the items `list`. + + :param inventory: dict - dictionary of existing inventory. + :param items: list - list of items to update the inventory with. + :return: dict - the inventory updated with the new items. + """ + + for item in items: + inventory.setdefault(item, 0) + inventory[item] += 1 + return inventory + + +def decrement_items(inventory, items): + """Decrement items in inventory using elements from the `items` list. + + :param inventory: dict - inventory dictionary. + :param items: list - list of items to decrement from the inventory. + :return: dict - updated inventory with items decremented. + """ + + for item in items: + if item in inventory: + inventory[item] = max(inventory[item] - 1, 0) + return inventory + + +def remove_item(inventory, item): + """Remove item from inventory if it matches `item` string. + + :param inventory: dict - inventory dictionary. + :param item: str - item to remove from the inventory. + :return: dict - updated inventory with item removed. Current inventory if item does not match. + """ + + if item in inventory: + inventory.pop(item) + return inventory + + +def list_inventory(inventory): + """Create a list containing only available (item_name, item_count > 0) pairs in inventory. + + :param inventory: dict - an inventory dictionary. + :return: list of tuples - list of key, value pairs from the inventory dictionary. + """ + + # [item for item in sorted(inventory.items()) if item[1] > 0] + + output = [] + for item in sorted(inventory.items()): + if item[1] > 0: + output.append(item) + + return output diff --git a/test/little-sisters-essay/.meta/config.json b/test/little-sisters-essay/.meta/config.json new file mode 100644 index 0000000..5049238 --- /dev/null +++ b/test/little-sisters-essay/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "kimolivia" + ], + "contributors": [ + "valentin-p", + "BethanyG" + ], + "files": { + "solution": [ + "string_methods.py" + ], + "test": [ + "string_methods_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "anagrams", + "blurb": "Learn about string methods while improving your little sister's school essay." +} diff --git a/test/little-sisters-essay/.meta/design.md b/test/little-sisters-essay/.meta/design.md new file mode 100644 index 0000000..b8678b9 --- /dev/null +++ b/test/little-sisters-essay/.meta/design.md @@ -0,0 +1,43 @@ +# Design + +## Design + +## Goal + +The goal of this exercise is to teach helpful string manipulation methods and how to use them in Python. + +## Learning objectives + +- Know the `str` class and its core methods +- Know when to use methods of str to operate on a string +- Use several of the methods from each grouping below to operate on strings +- Check if a given string ends/begins with the a specified suffix/prefix with the `endswith()`/ `startswith()` methods +- Check if a given string is composed of certain code points/characters by using `isalnum()`, `isalph()`, `isascii()`, `isdecimal()`, `isdigit()`, or `isnumeric()` methods. +- Return a new string that is a transform of the input string using `capitalize()`, `lower()`, `casefold()`, `swapcase()`, `upper()`, or `title()` methods +- Return a new string that has leading/trailing whitespaces removed with `lstrip()`, `rstrip()`, or `strip()` methods +- Check if a given substring is contained within a given string using `in` +- Return a new string that replaces a given substring with a new value using the `replace()` method +- Build a new string from an input iterable with `join()` + +## Out of scope + +- string formatting methods, "f-strings", "format strings" +- string splitting +- string translate methods (will have their own exercise) +- string constants from the string module +- built-ins that can take a str as an argument +- encoding/decoding & codecs +- the binary sequence types bytes, bytearray, and memoryview +- binary transforms and text transforms +- text encodings + +## Concepts + +- `string-methods` + +## Prerequisites + +- `booleans` +- `strings` +- `sequences`, `sequence type` +- `iteration` diff --git a/test/little-sisters-essay/.meta/exemplar.py b/test/little-sisters-essay/.meta/exemplar.py new file mode 100644 index 0000000..c51e930 --- /dev/null +++ b/test/little-sisters-essay/.meta/exemplar.py @@ -0,0 +1,45 @@ +"""Functions to help edit essay homework using string manipulation.""" + + +def capitalize_title(title): + """Convert the first letter of each word in the title to uppercase if needed. + + :param title: str - title string that needs title casing. + :return: str - title string in title case (first letters capitalized). + """ + + return title.title() + + +def check_sentence_ending(sentence): + """Check the ending of the sentence to verify that a period is present. + + :param sentence: str - a sentence to check. + :return: bool - is the sentence punctuated correctly? + """ + + return sentence.endswith(".") + + +def clean_up_spacing(sentence): + """Trim any leading or trailing whitespace from the sentence. + + :param sentence: str - a sentence to clean of leading and trailing space characters. + :return: str - a sentence that has been cleaned of leading and trailing space characters. + """ + + clean_sentence = sentence.strip() + return clean_sentence + + +def replace_word_choice(sentence, old_word, new_word): + """Replace a word in the provided sentence with a new one. + + :param sentence: str - a sentence to replace words in. + :param old_word: str - word to replace. + :param new_word: str - replacement word. + :return: str - input sentence with new words in place of old words. + """ + + better_sentence = sentence.replace(old_word, new_word) + return better_sentence diff --git a/test/little-sisters-essay/analysis.json b/test/little-sisters-essay/analysis.json new file mode 100644 index 0000000..464bec9 --- /dev/null +++ b/test/little-sisters-essay/analysis.json @@ -0,0 +1,10 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.general.general_recommendations", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/little-sisters-essay/string_methods.py b/test/little-sisters-essay/string_methods.py new file mode 100644 index 0000000..e25e680 --- /dev/null +++ b/test/little-sisters-essay/string_methods.py @@ -0,0 +1,38 @@ +"""Functions to help edit essay homework using string manipulation. +Consider writing a startswith/endswith custom checker/analyzer. +""" + + +def capitalize_title(title): + + return title.title() + + +def check_sentence_ending(sentence): + + # sentence.endswith(".") + return sentence[:-1] == "." + + +def clean_up_spacing(sentence): + """Trim any leading or trailing whitespace from the sentence. + + :param sentence: str - a sentence to clean of leading and trailing space characters. + :return: str - a sentence that has been cleaned of leading and trailing space characters. + """ + + clean_sentence = sentence.strip() + + return clean_sentence + + +def replace_word_choice(sentence, old_word, new_word): + """Replace a word in the provided sentence with a new one. + + :param sentence: str - a sentence to replace words in. + :param old_word: str - word to replace. + :param new_word: str - replacement word. + :return: str - input sentence with new words in place of old words. + """ + + return sentence.replace(old_word, new_word) diff --git a/test/little-sisters-vocab/.meta/config.json b/test/little-sisters-vocab/.meta/config.json new file mode 100644 index 0000000..2e1cd93 --- /dev/null +++ b/test/little-sisters-vocab/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "aldraco", + "BethanyG" + ], + "files": { + "solution": [ + "strings.py" + ], + "test": [ + "strings_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "two-fer", + "blurb": "Learn about strings by helping your little sister with her vocabulary homework." +} diff --git a/test/little-sisters-vocab/.meta/design.md b/test/little-sisters-vocab/.meta/design.md new file mode 100644 index 0000000..e6affd8 --- /dev/null +++ b/test/little-sisters-vocab/.meta/design.md @@ -0,0 +1,33 @@ +# Design + +## Goal + +The goal of this exercise is to teach the student about Python strings, and familiarize them with string manipulation in Python. + +## Things to teach + +- Know that Python has a `str` type. +- How to use `.join()` +- How to use `.split()` +- Know how to manipulate strings to create new strings. + +## Things not to teach + +- Regex: `regex`. That is for a different exercise. +- Iteration: Although strings are iterable, this is not the focus of this exercise. + + +## Prerequisites + +- `basics`: The student should be familiar with the basics exercise. + +## Representer + +This exercise does not require any logic to be added to the [representer][representer] + +## Analyzer + +This exercise does not require any logic to be added to the [analyzer][analyzer]. + +[analyzer]: https://github.com/exercism/python-analyzer +[representer]: https://github.com/exercism/python-representer diff --git a/test/little-sisters-vocab/.meta/exemplar.py b/test/little-sisters-vocab/.meta/exemplar.py new file mode 100644 index 0000000..c71d490 --- /dev/null +++ b/test/little-sisters-vocab/.meta/exemplar.py @@ -0,0 +1,68 @@ +"""Functions for creating, transforming, and adding prefixes to strings.""" + + +def add_prefix_un(word): + """Take the given word and add the 'un' prefix. + + :param word: str - containing the root word. + :return: str - of root word prepended with 'un'. + """ + + return 'un' + word + + +def make_word_groups(vocab_words): + """Transform a list containing a prefix and words into a string with the prefix followed by the words with prefix prepended. + + :param vocab_words: list - of vocabulary words with prefix in first index. + :return: str - of prefix followed by vocabulary words with + prefix applied. + + This function takes a `vocab_words` list and returns a string + with the prefix and the words with prefix applied, separated + by ' :: '. + + For example: list('en', 'close', 'joy', 'lighten'), + produces the following string: 'en :: enclose :: enjoy :: enlighten'. + """ + + prefix = vocab_words[0] + joiner = ' :: ' + prefix + + return joiner.join(vocab_words) + + +def remove_suffix_ness(word): + """Remove the suffix from the word while keeping spelling in mind. + + :param word: str - of word to remove suffix from. + :return: str - of word with suffix removed & spelling adjusted. + + For example: "heaviness" becomes "heavy", but "sadness" becomes "sad". + """ + + word = word[:-4] + if word[-1] == 'i': + word = word[:-1] + 'y' + + return word + + +def adjective_to_verb(sentence, index): + """Change the adjective within the sentence to a verb. + + :param sentence: str - that uses the word in sentence. + :param index: int - index of the word to remove and transform. + :return: str - word that changes the extracted adjective to a verb. + + For example, ("It got dark as the sun set", 2) becomes "darken". + """ + + word = sentence.split()[index] + + if word[-1] == '.': + word = word[:-1] + 'en' + else: + word = word + 'en' + + return word diff --git a/test/little-sisters-vocab/analysis.json b/test/little-sisters-vocab/analysis.json new file mode 100644 index 0000000..464bec9 --- /dev/null +++ b/test/little-sisters-vocab/analysis.json @@ -0,0 +1,10 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.general.general_recommendations", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/little-sisters-vocab/strings.py b/test/little-sisters-vocab/strings.py new file mode 100644 index 0000000..c71d490 --- /dev/null +++ b/test/little-sisters-vocab/strings.py @@ -0,0 +1,68 @@ +"""Functions for creating, transforming, and adding prefixes to strings.""" + + +def add_prefix_un(word): + """Take the given word and add the 'un' prefix. + + :param word: str - containing the root word. + :return: str - of root word prepended with 'un'. + """ + + return 'un' + word + + +def make_word_groups(vocab_words): + """Transform a list containing a prefix and words into a string with the prefix followed by the words with prefix prepended. + + :param vocab_words: list - of vocabulary words with prefix in first index. + :return: str - of prefix followed by vocabulary words with + prefix applied. + + This function takes a `vocab_words` list and returns a string + with the prefix and the words with prefix applied, separated + by ' :: '. + + For example: list('en', 'close', 'joy', 'lighten'), + produces the following string: 'en :: enclose :: enjoy :: enlighten'. + """ + + prefix = vocab_words[0] + joiner = ' :: ' + prefix + + return joiner.join(vocab_words) + + +def remove_suffix_ness(word): + """Remove the suffix from the word while keeping spelling in mind. + + :param word: str - of word to remove suffix from. + :return: str - of word with suffix removed & spelling adjusted. + + For example: "heaviness" becomes "heavy", but "sadness" becomes "sad". + """ + + word = word[:-4] + if word[-1] == 'i': + word = word[:-1] + 'y' + + return word + + +def adjective_to_verb(sentence, index): + """Change the adjective within the sentence to a verb. + + :param sentence: str - that uses the word in sentence. + :param index: int - index of the word to remove and transform. + :return: str - word that changes the extracted adjective to a verb. + + For example, ("It got dark as the sun set", 2) becomes "darken". + """ + + word = sentence.split()[index] + + if word[-1] == '.': + word = word[:-1] + 'en' + else: + word = word + 'en' + + return word diff --git a/test/locomotive-engineer/.meta/config.json b/test/locomotive-engineer/.meta/config.json new file mode 100644 index 0000000..e9110e9 --- /dev/null +++ b/test/locomotive-engineer/.meta/config.json @@ -0,0 +1,25 @@ +{ + "authors": [ + "meatball133", + "BethanyG" + ], + "contributors": [ + "IsaacG" + ], + "files": { + "solution": [ + "locomotive_engineer.py" + ], + "test": [ + "locomotive_engineer_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "forked_from": [ + "javascript/train-driver" + ], + "icon": "tracks-on-tracks-on-tracks", + "blurb": "Learn about unpacking and multiple assignment in Python while helping Linus with his train control system." +} diff --git a/test/locomotive-engineer/.meta/design.md b/test/locomotive-engineer/.meta/design.md new file mode 100644 index 0000000..933b7cb --- /dev/null +++ b/test/locomotive-engineer/.meta/design.md @@ -0,0 +1,62 @@ +# Design + +## Goal + +This concept exercise is meant to teach an understanding/use of `unpacking` and the `*` (splat) and `**` (double splat) operators in Python. + +
+ +## Learning objectives + +- Understand/use `unpacking` through the use of `*` and `**` _prefix_ operators in various scenarios + - `*` and `**` as _prefixes_ ..... not to be confused with `*` (_multiply_) and `**` (_exponentiation_) as _infix_, or mathematical operators (**consider a link in the links doc or a mention in dig deeper.**) + - use in arguments to `functions` + - use in argument _capture_ for `functions` (_aka passing an arbitrary number of arguments -- *args * & \*\*kwargs_) + - use in iterable (_mainly `tuple` and `list`_) unpacking & packing + - use in `dict` unpacking & packing +- Understand/use `unpacking` via `multiple assignment` + - using `multiple assignment ` in place of `indexing` + - using `multiple assignment` + `*` in place of `slicing` + - unpacking plus "leftovers" via `*` +- Differences between straight `multiple assignment` and `*` & `**` +- Deep unpacking + +## Concepts + +- `unpacking` +- `unpacking generalizations` +- `multiple assignment` + +## Topics that are Out of scope + +- `classes` +- `comprehensions` +- `comprehensions` in `lambdas` +- `map()`, `filter()` or `functools.reduce()` in a `comprehension` +- `function-arguments` beyond explaining briefly how `*`, `**` work in function arguments. +- `functools` beyond `functools.reduce()`(_this will get its own exercise_) +- `generators` +- using an `assignment expression` or "walrus" operator (`:=`) alone or in a `lambda` + +## Prerequisites + +- `basics` +- `bools` +- `comparisons` +- `dicts` +- `lists` +- `numbers` +- `strings` +- `tuples` +- `loops` + +## Representer + +This exercise does not require any specific logic to be added to the [representer][representer] + +## Analyzer + +This exercise does not require any specific logic to be added to the [analyzer][analyzer]. + +[analyzer]: https://github.com/exercism/python-analyzer +[representer]: https://github.com/exercism/python-representer diff --git a/test/locomotive-engineer/.meta/exemplar.py b/test/locomotive-engineer/.meta/exemplar.py new file mode 100644 index 0000000..bda295d --- /dev/null +++ b/test/locomotive-engineer/.meta/exemplar.py @@ -0,0 +1,58 @@ +"""Functions which helps the locomotive engineer to keep track of the train.""" + + +def get_list_of_wagons(*args): + """Return a list of wagons. + + :param *args: arbitrary number of wagons. + :return: list - list of wagons. + """ + + return list(args) + + +def fix_list_of_wagons(each_wagons_id, missing_wagons): + """Fix the list of wagons. + + :param each_wagons_id: list - the list of wagons. + :param missing_wagons: list - the list of missing wagons. + :return: list - list of wagons. + """ + + first, second, locomotive, *rest = each_wagons_id + + return [locomotive, *missing_wagons, *rest, first, second] + + +def add_missing_stops(route, **kwargs): + """Add missing stops to route dict. + + :param route: dict - the dict of routing information. + :param **kwargs: arbitrary number of stops. + :return: dict - updated route dictionary. + """ + + return {**route, "stops": list(kwargs.values())} + + +def extend_route_information(route, more_route_information): + """Extend route information with more_route_information. + + :param route: dict - the route information. + :param more_route_information: dict - extra route information. + :return: dict - extended route information. + """ + + return {**route, **more_route_information} + + +def fix_wagon_depot(wagons_rows): + """Fix the list of rows of wagons. + + :param wagons_rows: list[tuple] - the list of rows of wagons. + :return: list[tuple] - list of rows of wagons. + """ + + [*row_one], [*row_two], [*row_three] = zip(*wagons_rows) + + return [row_one, row_two, row_three] diff --git a/test/locomotive-engineer/analysis.json b/test/locomotive-engineer/analysis.json new file mode 100644 index 0000000..464bec9 --- /dev/null +++ b/test/locomotive-engineer/analysis.json @@ -0,0 +1,10 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.general.general_recommendations", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/locomotive-engineer/locomotive_engineer.py b/test/locomotive-engineer/locomotive_engineer.py new file mode 100644 index 0000000..db8e1a0 --- /dev/null +++ b/test/locomotive-engineer/locomotive_engineer.py @@ -0,0 +1,58 @@ +"""Functions which helps the locomotive engineer to keep track of the train.""" + + +def get_list_of_wagons(*args): + """Return a list of wagons. + + :param *args: arbitrary number of wagons. + :return: list - list of wagons. + """ + + return list(args) + + +def fix_list_of_wagons(each_wagons_id, missing_wagons): + """Fix the list of wagons. + + :param each_wagons_id: list - the list of wagons. + :param missing_wagons: list - the list of missing wagons. + :return: list - list of wagons. + """ + + first, second, locomotive, *rest = each_wagons_id + + return [locomotive, *missing_wagons, *rest, first, second] + + +def add_missing_stops(route, **kwargs): + """Add missing stops to route dict. + + :param route: dict - the dict of routing information. + :param **kwargs: arbitrary number of stops. + :return: dict - updated route dictionary. + """ + + return {**route, "stops": list(kwargs.values())} + + +def extend_route_information(route, more_route_information): + """Extend route information with more_route_information. + + :param route: dict - the route information. + :param more_route_information: dict - extra route information. + :return: dict - extended route information. + """ + + return {**route, **more_route_information} + + +def fix_wagon_depot(wagons_rows): + """Fix the list of rows of wagons. + + :param wagons_rows: list[tuple] - the list of rows of wagons. + :return: list[tuple] - list of rows of wagons. + """ + + [*row_one], [*row_two], [*row_three] = zip(*wagons_rows) + + return list(row_one, row_two, row_three) diff --git a/test/log-levels/.meta/config.json b/test/log-levels/.meta/config.json new file mode 100644 index 0000000..fc14d0e --- /dev/null +++ b/test/log-levels/.meta/config.json @@ -0,0 +1,24 @@ +{ + "authors": [ + "mohanrajanr" + ], + "contributors": [ + "valentin-p" + ], + "files": { + "solution": [ + "enums.py" + ], + "test": [ + "enums_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "forked_from": [ + "csharp/logs-logs-logs" + ], + "icon": "log-levels", + "blurb": "Learn about enums by automating your tedious logging-level tasks." +} diff --git a/test/log-levels/.meta/design.md b/test/log-levels/.meta/design.md new file mode 100644 index 0000000..df43fbe --- /dev/null +++ b/test/log-levels/.meta/design.md @@ -0,0 +1,72 @@ +# Design + +## Goal + +The goal of this exercise is to teach the student how Enums ([Enumerations](https://en.wikipedia.org/wiki/Enumerated_type)) are implemented & used in Python. We will teach this through Pythons Enum class/type. + +## Learning objectives + +- What is an enumeration / enum is, and why they are needed/wanted in Python? +- Understand the nomenclature (naming) used in reference to enums (e.g. enumeration/enum, enum members, enum member names, and enum member values) +- Understand that enumeration members are functionally constants (and therefore should be formatted as such) +- How Enums are different from other, more generic Python classes. +- How to create various Enums + - By using the class syntax by importing & subclassing Enum. + - By using the Enum Functional API + - By defining enum members with values that include non-int types (like str, tuple, float etc.) + - Using the auto() function to automatically assign integer values to members when exact value types are unimportant/not needed. + - Creating member aliases by assigning the same value to different names (two different names can have the same value; the second name defined becomes an alias of the first) + - Using the class decorator @enum.unique to prevent member aliases and enforce that member values be unique. +- How to use an enumeration and enumeration members in other functions/code + - Enum members are iterable in member definition order, but iteration will not include aliases. + - An ordered mapping of names to members can be retrieved via **members**.items() + - enumeration members are compared by identity, using the is/is not keywords + - Ordered comparison (<, >, <=, '>=) between enumeration values is not supported, and will throw aTypeError`. + - Equality/inequality comparison is defined, and == and != can be used. + - Comparisons between enumeration values and non-enumeration values will always return False + +## Out of scope + +- Flag enum subtype (perhaps better as an advanced exercise that includes bitwise operations) +- IntEnum and IntFlag subtypes (since they break the semantic promises of an enum by being comparable to int) +- mixins & multiple inheritance for the creation of Enums +- using **new**() to customize the value of an enum member (better for an advanced/extended enum exercise) +- omitting values +- subclassing an "empty" pre-defined enum +- customization of auto() +- Pickling + +## Concepts + +- enumeration, enums + +## Prerequisites + +- `decorators, @` +- `__init__()` +- `classes, OOP` +- `inheritance` +- `iteration` +- `iterables` +- `dunder methods` +- `comparisons` +- `rich comparisons` +- `class attributes` +- `importing` +- `aliasing` +- `dicts, dict methods (specifically dict.items())` +- `mapping types` +- `immutable, immutability` +- `class properties` + +## Resources + +- [Exercism v3 C# Enums concept exercise](https://github.com/exercism/v3/tree/master/languages/csharp/exercises/concept/enums) +- [Python Docs: Enum](https://docs.python.org/3/library/enum.html) +- [Enum - Mouse vs. Python](https://www.blog.pythonlibrary.org/2018/03/20/python-3-an-intro-to-enumerations/) +- [Why you should use more enums in Python - Florian Dahlitz](https://florian-dahlitz.de/blog/why-you-should-use-more-enums-in-python) +- [Python Docs: How are Enums Different?](https://docs.python.org/3/library/enum.html#how-are-enums-different) +- [Python Docs: The Enum Functional API](https://docs.python.org/3/library/enum.html#functional-api) +- [Stack Overflow: Python enum, when and where to use?](https://stackoverflow.com/questions/22586895/python-enum-when-and-where-to-use) +- [PEP435: Adding an Enum Type to the Python Standard Library](https://www.python.org/dev/peps/pep-0435/) +- [Using Enums and Django model Choices - Ben Cleary](https://medium.com/@bencleary/using-enums-as-django-model-choices-96c4cbb78b2e) diff --git a/test/log-levels/.meta/exemplar.py b/test/log-levels/.meta/exemplar.py new file mode 100644 index 0000000..db45eeb --- /dev/null +++ b/test/log-levels/.meta/exemplar.py @@ -0,0 +1,73 @@ +from enum import Enum + + +class LogLevel(Enum): + """Represent different log levels by their verbose codes.""" + + TRACE = 'TRC' + DEBUG = 'DBG' + INFO = 'INF' + WARNING = 'WRN' + WARN = 'WRN' + ERROR = 'ERR' + FATAL = 'FTL' + UNKNOWN = 'UKN' + + +class LogLevelInt(Enum): + """Represent different log levels by their short codes.""" + + TRACE = 0 + DEBUG = 1 + INFO = 4 + WARNING = 5 + WARN = 5 + ERROR = 6 + FATAL = 7 + UNKNOWN = 42 + + +def parse_log_level(message): + """Return level enum for log message. + + :param message: log message (string) + :return: enum - 'LogLevel.'. Return 'LogLevel.Unknown' if an unknown severity is passed. + """ + + str_split = message.split(':') + lvl = str_split[0][1:-1] + if lvl in [level.value for level in LogLevel]: + return LogLevel(lvl) + return LogLevel('UKN') + + +def convert_to_short_log(log_level, message): + """Convert a log message to its shorter format. + + :param log_level: enum - 'LogLevel.' e.g. 'LogLevel.Error' + :param message: str - log message + :return: enum - 'LogLevelInt.` e.g. 'LogLevelInt.5' + """ + + return f'{LogLevelInt[log_level.name].value}:{message}' + + +def get_warn_alias(): + """Returns the enum for LogLevel.Warning. + + :return: enum - 'LogLevel'.' + """ + + return LogLevel('WRN') + + +def get_members(): + """Return all members of the enum. + + :return: list of tuples - [(name1, value1), (name2, value2)] + """ + + out_list = [] + for member in LogLevel: + out_list.append((member.name, member.value)) + return out_list diff --git a/test/log-levels/analysis.json b/test/log-levels/analysis.json new file mode 100644 index 0000000..3710881 --- /dev/null +++ b/test/log-levels/analysis.json @@ -0,0 +1,18 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.pylint.convention", + "params": { + "lineno": "1", + "code": "C0114 missing-module-docstring", + "message": "Missing module docstring", + "bad_code": "Instead of: \n```python\nimport sys # [missing-module-docstring]\n\n\ndef print_python_version():\n print(sys.version)\n```\n\n", + "good_code": "Try: \n```python\n\"\"\"Module providing a function printing python version.\"\"\"\n\nimport sys\n\n\ndef print_python_version():\n print(sys.version)\n```\n\n", + "related_info": null, + "details": null + }, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/log-levels/enums.py b/test/log-levels/enums.py new file mode 100644 index 0000000..db45eeb --- /dev/null +++ b/test/log-levels/enums.py @@ -0,0 +1,73 @@ +from enum import Enum + + +class LogLevel(Enum): + """Represent different log levels by their verbose codes.""" + + TRACE = 'TRC' + DEBUG = 'DBG' + INFO = 'INF' + WARNING = 'WRN' + WARN = 'WRN' + ERROR = 'ERR' + FATAL = 'FTL' + UNKNOWN = 'UKN' + + +class LogLevelInt(Enum): + """Represent different log levels by their short codes.""" + + TRACE = 0 + DEBUG = 1 + INFO = 4 + WARNING = 5 + WARN = 5 + ERROR = 6 + FATAL = 7 + UNKNOWN = 42 + + +def parse_log_level(message): + """Return level enum for log message. + + :param message: log message (string) + :return: enum - 'LogLevel.'. Return 'LogLevel.Unknown' if an unknown severity is passed. + """ + + str_split = message.split(':') + lvl = str_split[0][1:-1] + if lvl in [level.value for level in LogLevel]: + return LogLevel(lvl) + return LogLevel('UKN') + + +def convert_to_short_log(log_level, message): + """Convert a log message to its shorter format. + + :param log_level: enum - 'LogLevel.' e.g. 'LogLevel.Error' + :param message: str - log message + :return: enum - 'LogLevelInt.` e.g. 'LogLevelInt.5' + """ + + return f'{LogLevelInt[log_level.name].value}:{message}' + + +def get_warn_alias(): + """Returns the enum for LogLevel.Warning. + + :return: enum - 'LogLevel'.' + """ + + return LogLevel('WRN') + + +def get_members(): + """Return all members of the enum. + + :return: list of tuples - [(name1, value1), (name2, value2)] + """ + + out_list = [] + for member in LogLevel: + out_list.append((member.name, member.value)) + return out_list diff --git a/test/making-the-grade/.meta/config.json b/test/making-the-grade/.meta/config.json new file mode 100644 index 0000000..d9edd46 --- /dev/null +++ b/test/making-the-grade/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "mohanrajanr", + "BethanyG" + ], + "contributors": [ + "pranasziaukas" + ], + "files": { + "solution": [ + "loops.py" + ], + "test": [ + "loops_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "grep", + "blurb": "Learn about loops by grading and organizing your students exam scores." +} diff --git a/test/making-the-grade/.meta/design.md b/test/making-the-grade/.meta/design.md new file mode 100644 index 0000000..88ead03 --- /dev/null +++ b/test/making-the-grade/.meta/design.md @@ -0,0 +1,39 @@ +# Design + +## Goal + +This concept exercise should convey a basic understanding of how to handle the `for` loop and `while` in Python. This concept exercise should also teach how to interrupt or change the normal flow of a loop using the `break` statement and the `continue` statement. + +## Learning objectives + +- use `while` to make a loop: execute something as long as an expression/test is `true` +- use `for` to iterate over an iterable object with the `for ... in` pattern. The Membership testing concept (in) is required to address this concept. +- understand the differences between `while` (indefinite, condition-dependent) and `for` (definite, length or count dependent) loops +- use `break` statement to terminate the nearest enclosing loop +- use `continue` statement to continue with the next cycle of the nearest enclosing loop + +## Out of scope + +- async loops (advanced) + +## Concepts + +- `while` loops +- `for` loops +- `break` statement +- `continue` statement + +## Prerequisites + +- `basics` +- `numbers` +- `arithmetic` + +## Resources + +- [The while key](https://yawpitchroll.com/posts/the-35-words-you-need-to-python/#while) +- [The for loop](https://yawpitchroll.com/posts/the-35-words-you-need-to-python/#for) +- [The break statement](https://yawpitchroll.com/posts/the-35-words-you-need-to-python/#break) +- [The break statement python docs](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement) +- [The continue statement](https://yawpitchroll.com/posts/the-35-words-you-need-to-python/#continue) +- [The continue statement python docs](https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement) diff --git a/test/making-the-grade/.meta/exemplar.py b/test/making-the-grade/.meta/exemplar.py new file mode 100644 index 0000000..5aa7508 --- /dev/null +++ b/test/making-the-grade/.meta/exemplar.py @@ -0,0 +1,98 @@ +"""Functions for organizing and calculating student exam scores.""" + + +def round_scores(student_scores): + """Round all provided student scores. + + :param student_scores: list - float or int of student exam scores. + :return: list - student scores *rounded* to nearest integer value. + """ + + rounded = [] + while student_scores: + rounded.append(round(student_scores.pop())) + return rounded + + +def count_failed_students(student_scores): + """Count the number of failing students out of the group provided. + + :param student_scores: list - containing int student scores. + :return: int - count of student scores at or below 40. + """ + + non_passing = 0 + for score in student_scores: + if score <= 40: + non_passing += 1 + return non_passing + + +def above_threshold(student_scores, threshold): + """Determine how many of the provided student scores were 'the best' based on the provided threshold. + + :param student_scores: list - of integer scores. + :param threshold: int - threshold to cross to be the "best" score. + :return: list - of integer scores that are at or above the "best" threshold. + """ + + above = [] + + for score in student_scores: + if score >= threshold: + above.append(score) + return above + + +def letter_grades(highest): + """Create a list of grade thresholds based on the provided highest grade. + + :param highest: int - value of highest exam score. + :return: list - of lower threshold scores for each D-A letter grade interval. + For example, where the highest score is 100, and failing is <= 40, + The result would be [41, 56, 71, 86]: + + 41 <= "D" <= 55 + 56 <= "C" <= 70 + 71 <= "B" <= 85 + 86 <= "A" <= 100 + """ + + increment = round((highest - 40) / 4) + scores = [] + for score in range(41, highest, increment): + scores.append(score) + return scores + + +def student_ranking(student_scores, student_names): + """Organize the student's rank, name, and grade information in ascending order. + + :param student_scores: list - of scores in descending order. + :param student_names: list - of string names by exam score in descending order. + :return: list - of strings in format [". : "]. + """ + + results = [] + for index, name in enumerate(student_names): + rank_string = str(index + 1) + ". " + name + ": " + str(student_scores[index]) + results.append(rank_string) + + return results + + +def perfect_score(student_info): + """Create a list that contains the name and grade of the first student to make a perfect score on the exam. + + :param student_info: list - of [, ] lists. + :return: list - first `[, 100]` or `[]` if no student score of 100 is found. + """ + + result = [] + + for item in student_info: + if item[1] == 100: + result = item + break + + return result diff --git a/test/making-the-grade/analysis.json b/test/making-the-grade/analysis.json new file mode 100644 index 0000000..f127bf0 --- /dev/null +++ b/test/making-the-grade/analysis.json @@ -0,0 +1,18 @@ +{ + "summary": "There are a few suggested changes that can bring your solution closer to ideal.", + "comments": [ + { + "comment": "python.pylint.refactor", + "params": { + "lineno": "24", + "code": "R1728 consider-using-generator", + "message": "Consider using a generator instead 'sum(1 for score in student_scores if score <= 40)'", + "bad_code": "Instead of: \n```python\nlist([0 for y in list(range(10))]) # [consider-using-generator]\ntuple([0 for y in list(range(10))]) # [consider-using-generator]\nsum([y**2 for y in list(range(10))]) # [consider-using-generator]\nmax([y**2 for y in list(range(10))]) # [consider-using-generator]\nmin([y**2 for y in list(range(10))]) # [consider-using-generator]\n```\n\n", + "good_code": "Try: \n```python\nlist(0 for y in list(range(10)))\ntuple(0 for y in list(range(10)))\nsum(y**2 for y in list(range(10)))\nmax(y**2 for y in list(range(10)))\nmin(y**2 for y in list(range(10)))\n```\n\n", + "related_info": "- [PEP 289](https://peps.python.org/pep-0289/)\n- [Benchmark and discussion for\n any/all/list/tuple](https://github.com/pylint-dev/pylint/pull/3309#discussion_r576683109)\n- [Benchmark and discussion for\n sum/max/min](https://github.com/pylint-dev/pylint/pull/6595#issuecomment-1125704244)\n", + "details": "Removing `[]` inside calls that can use containers or generators should\nbe considered for performance reasons since a generator will have an\nupfront cost to pay. The performance will be better if you are working\nwith long lists or sets.\n\nFor `max`, `min` and `sum` using a generator is also recommended by\npep289.\n" + }, + "type": "actionable" + } + ] +} \ No newline at end of file diff --git a/test/making-the-grade/loops.py b/test/making-the-grade/loops.py new file mode 100644 index 0000000..1a9ccea --- /dev/null +++ b/test/making-the-grade/loops.py @@ -0,0 +1,99 @@ +"""Functions for organizing and calculating student exam scores.""" + + +def round_scores(student_scores): + """Round all provided student scores. + + :param student_scores: list - float or int of student exam scores. + :return: list - student scores *rounded* to nearest integer value. + """ + + rounded = [] + while student_scores: + rounded.append(round(student_scores.pop())) + return rounded + + +def count_failed_students(student_scores): + """Count the number of failing students out of the group provided. + + :param student_scores: list - containing int student scores. + :return: int - count of student scores at or below 40. + """ + + non_passing = sum([1 for score in student_scores if score <= 40]) + # for score in student_scores: + # if score <= 40: + # non_passing += 1 + return non_passing + + +def above_threshold(student_scores, threshold): + """Determine how many of the provided student scores were 'the best' based on the provided threshold. + + :param student_scores: list - of integer scores. + :param threshold: int - threshold to cross to be the "best" score. + :return: list - of integer scores that are at or above the "best" threshold. + """ + + above = [score for score in student_scores if score >= threshold] + + # for score in student_scores: + # if score >= threshold: + # above.append(score) + return above + + +def letter_grades(highest): + """Create a list of grade thresholds based on the provided highest grade. + + :param highest: int - value of highest exam score. + :return: list - of lower threshold scores for each D-A letter grade interval. + For example, where the highest score is 100, and failing is <= 40, + The result would be [41, 56, 71, 86]: + + 41 <= "D" <= 55 + 56 <= "C" <= 70 + 71 <= "B" <= 85 + 86 <= "A" <= 100 + """ + + increment = round((highest - 40) / 4) + scores = [] + for score in range(41, highest, increment): + scores.append(score) + return scores + + +def student_ranking(student_scores, student_names): + """Organize the student's rank, name, and grade information in ascending order. + + :param student_scores: list - of scores in descending order. + :param student_names: list - of string names by exam score in descending order. + :return: list - of strings in format [". : "]. + """ + + # results = ['f{index+1}. {name}: {student_scores[index]}' for index, name in enumerate(student_names)] + + # for index, name in enumerate(student_names): + # rank_string = str(index + 1) + ". " + name + ": " + str(student_scores[index]) + # results.append(rank_string) + + return list(f'{index+1}. {name}: {student_scores[index]}' for index, name in enumerate(student_names)) + + +def perfect_score(student_info): + """Create a list that contains the name and grade of the first student to make a perfect score on the exam. + + :param student_info: list - of [, ] lists. + :return: list - first `[, 100]` or `[]` if no student score of 100 is found. + """ + + result = [] + + for item in student_info: + if item[1] == 100: + result = item + break + + return result diff --git a/test/mecha-munch-management/.meta/config.json b/test/mecha-munch-management/.meta/config.json new file mode 100644 index 0000000..b75803a --- /dev/null +++ b/test/mecha-munch-management/.meta/config.json @@ -0,0 +1,24 @@ +{ + "authors": [ + "meatball133", + "BethanyG" + ], + "contributors": [ + ], + "files": { + "solution": [ + "dict_methods.py" + ], + "test": [ + "dict_methods_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ], + "editor": [ + "dict_methods_test_data.py" + ] + }, + "icon": "gross-store", + "blurb": "Learn about dictionary methods by building a shopping cart MVP for the Mecha Munch grocery app." +} diff --git a/test/mecha-munch-management/.meta/design.md b/test/mecha-munch-management/.meta/design.md new file mode 100644 index 0000000..4c3c52a --- /dev/null +++ b/test/mecha-munch-management/.meta/design.md @@ -0,0 +1,59 @@ +## Learning objectives + +Cover useful `dict` methods and a few techniques for operating on/manipulating `dicts`. + +- `dict.setdefault()` for automatically adding keys when needed. +- `dict.fromkeys(iterable, )` for creating a new `dict` from any number of iterables. +- `dict.keys()`, `dict.values()`, and `dict.items()` for convenient iterators. +- `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())` for reversed views. +- `sorted()` with `dict.items()`. for re-ordering entries in a `dict`. +- `dict_one.update()` for updating one `dict` with overlapping values from another `dict`. +- `dict | other_dict` and `dict |= other_dict` for merging or updating two `dict`s via operators. +- `dict.popitem()` for removing and returning a key, value pair. + +- Working more with the `dict` views `items()` , `keys()` or `values()`. (e.g, by sorting information using `sorted()` or by swapping `keys` and `values`, etc.) +- Knowing that Dictionaries can be _nested_, _-- e.g._ ' a dictionary of dictionaries'. +- Considerations when `updating()` or using `union` with dictionaries. + +## Out of scope + +Please take a look at the `dicts` concept exercise [design.md file](https://github.com/exercism/python/edit/main/exercises/concept/inventory-management/.meta/design.md) for `dict` features taught thus far. +While those methods can be used for solutions to this exercise, it isn't necessary to cover them again in detail. Additionally, the following is out of scope: + +- Dictionary comprehensions +- Built-in functions as they relate to this data structure (*e.g.* `len()`, or `enumerate()` +- Considerations of Mutability +- `copy()` vs `deepcopy()` +- Memory and performance characteristics. +- Related `collections` module with `Counter()` and `defaultdict()` + +## Concepts + +- `dicts` +- `dict-methods` + +## Prerequisites + +These are the concepts/concept exercises the student needs to complete/understand before solving this concept exercise. + +- `basics` +- `bools` +- `conditionals` +- `comparisons` +- `dicts` +- `lists` +- `loops` +- `numbers` +- `strings` +- `tuples` + + +## Resources to refer to + +- [Python docs: Tutorial - Dictionaries](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) +- [Python docs: Mapping Type `dict`](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict) +- [Real Python: Dicts](https://realpython.com/python-dicts/) +- [Digital Ocean: Understanding dictionaries in python 3](https://www.digitalocean.com/community/tutorials/understanding-dictionaries-in-python-3) +- [Stack Overflow: exchanging keys with values in a `dict` in Python](https://stackoverflow.com/questions/1031851/how-do-i-exchange-keys-with-values-in-a-dictionary) +- [kite: how to sort a dictionary by key in python](https://www.kite.com/python/answers/how-to-sort-a-dictionary-by-key-in-python) +- [medium: 16 Python Dictionary Tips](https://medium.com/python-in-plain-english/16-intermediate-level-python-dictionary-tips-tricks-and-shortcuts-1376859e1adc) _**note:** this is a good resource for ideas and writing this exericse, but is a subscription-based service, so not the best for linking to_ \ No newline at end of file diff --git a/test/mecha-munch-management/.meta/exemplar.py b/test/mecha-munch-management/.meta/exemplar.py new file mode 100644 index 0000000..ea25110 --- /dev/null +++ b/test/mecha-munch-management/.meta/exemplar.py @@ -0,0 +1,79 @@ +"""Functions to manage a users shopping cart items.""" + + +def add_item(current_cart, items_to_add): + """Add items to shopping cart. + + :param current_cart: dict - the current shopping cart. + :param items_to_add: iterable - items to add to the cart. + :return: dict - the updated user cart dictionary. + """ + + for item in items_to_add: + current_cart.setdefault(item, 0) + current_cart[item] += 1 + + return current_cart + + +def read_notes(notes): + """Create user cart from an iterable notes entry. + + :param notes: iterable of items to add to cart. + :return: dict - a user shopping cart dictionary. + """ + + return dict.fromkeys(notes, 1) + + +def update_recipes(ideas, recipe_updates): + """Update the recipe ideas dictionary. + + :param ideas: dict - The "recipe ideas" dict. + :param recipe_updates: dict - dictionary with updates for the ideas section. + :return: dict - updated "recipe ideas" dict. + """ + + ideas.update(recipe_updates) + return ideas + + +def sort_entries(cart): + """Sort a users shopping cart in alphabetically order. + + :param cart: dict - a users shopping cart dictionary. + :return: dict - users shopping cart sorted in alphabetical order. + """ + + return dict(sorted(cart.items())) + + +def send_to_store(cart, aisle_mapping): + """Combine users order to aisle and refrigeration information. + + :param cart: dict - users shopping cart dictionary. + :param aisle_mapping: dict - aisle and refrigeration information dictionary. + :return: dict - fulfillment dictionary ready to send to store. + """ + fulfillment_cart = {} + + for key in cart.keys(): + fulfillment_cart[key] = [cart[key]] + aisle_mapping[key] + + return dict(sorted(fulfillment_cart.items(), reverse=True)) + + +def update_store_inventory(fulfillment_cart, store_inventory): + """Update store inventory levels with user order. + + :param fulfillment cart: dict - fulfillment cart to send to store. + :param store_inventory: dict - store available inventory + :return: dict - store_inventory updated. + """ + + for key, values in fulfillment_cart.items(): + store_inventory[key][0] = store_inventory[key][0] - values[0] + if store_inventory[key][0] == 0: + store_inventory[key][0] = 'Out of Stock' + + return store_inventory diff --git a/test/mecha-munch-management/analysis.json b/test/mecha-munch-management/analysis.json new file mode 100644 index 0000000..1d52799 --- /dev/null +++ b/test/mecha-munch-management/analysis.json @@ -0,0 +1,31 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.pylint.convention", + "params": { + "lineno": "4", + "code": "C0116 missing-function-docstring", + "message": "Missing function or method docstring", + "bad_code": "Instead of: \n```python\nimport sys\n\n\ndef print_python_version(): # [missing-function-docstring]\n print(sys.version)\n```\n\n", + "good_code": "Try: \n```python\nimport sys\n\n\ndef print_python_version():\n \"\"\"Function printing python version.\"\"\"\n print(sys.version)\n```\n\n", + "related_info": null, + "details": null + }, + "type": "informative" + }, + { + "comment": "python.pylint.convention", + "params": { + "lineno": "61", + "code": "C0116 missing-function-docstring", + "message": "Missing function or method docstring", + "bad_code": "Instead of: \n```python\nimport sys\n\n\ndef print_python_version(): # [missing-function-docstring]\n print(sys.version)\n```\n\n", + "good_code": "Try: \n```python\nimport sys\n\n\ndef print_python_version():\n \"\"\"Function printing python version.\"\"\"\n print(sys.version)\n```\n\n", + "related_info": null, + "details": null + }, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/mecha-munch-management/dict_methods.py b/test/mecha-munch-management/dict_methods.py new file mode 100644 index 0000000..576da9c --- /dev/null +++ b/test/mecha-munch-management/dict_methods.py @@ -0,0 +1,69 @@ +"""Functions to manage a users shopping cart items.""" + + +def add_item(current_cart, items_to_add): + + for item in items_to_add: + current_cart.setdefault(item, 0) + current_cart[item] += 1 + + return current_cart + + +def read_notes(notes): + """Create user cart from an iterable notes entry. + + :param notes: iterable of items to add to cart. + :return: dict - a user shopping cart dictionary. + """ + + return dict.fromkeys(notes, 1) + + +def update_recipes(ideas, recipe_updates): + """Update the recipe ideas dictionary. + + :param ideas: dict - The "recipe ideas" dict. + :param recipe_updates: dict - dictionary with updates for the ideas section. + :return: dict - updated "recipe ideas" dict. + """ + + ideas.update(recipe_updates) + return ideas + + +def sort_entries(cart): + """Sort a users shopping cart in alphabetically order. + + :param cart: dict - a users shopping cart dictionary. + :return: dict - users shopping cart sorted in alphabetical order. + """ + + return dict(sorted(cart.items())) + + +def send_to_store(cart, aisle_mapping): + """Combine users order to aisle and refrigeration information. + + :param cart: dict - users shopping cart dictionary. + :param aisle_mapping: dict - aisle and refrigeration information dictionary. + :return: dict - fulfillment dictionary ready to send to store. + """ + + fulfillment_cart = {} + + for key in cart.keys(): + fulfillment_cart[key] = [cart[key]] + aisle_mapping[key] + + return dict(sorted(fulfillment_cart.items(), reverse=True)) + + +def update_store_inventory(fulfillment_cart, store_inventory): + + + for key, values in fulfillment_cart.items(): + store_inventory[key][0] = store_inventory[key][0] - values[0] + if store_inventory[key][0] == 0: + store_inventory[key][0] = 'Out of Stock' + + return store_inventory diff --git a/test/meltdown-mitigation/.meta/config.json b/test/meltdown-mitigation/.meta/config.json new file mode 100644 index 0000000..eb88d8b --- /dev/null +++ b/test/meltdown-mitigation/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "sachsom95", + "BethanyG" + ], + "contributors": [ + "kbuc" + ], + "files": { + "solution": [ + "conditionals.py" + ], + "test": [ + "conditionals_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "circular-buffer", + "blurb": "Learn about conditionals and avoid a meltdown by developing a simple control system for a Nuclear Reactor." +} diff --git a/test/meltdown-mitigation/.meta/design.md b/test/meltdown-mitigation/.meta/design.md new file mode 100644 index 0000000..33ef7e3 --- /dev/null +++ b/test/meltdown-mitigation/.meta/design.md @@ -0,0 +1,79 @@ +# Design + + +## Goal + +The goal of this exercise is to teach the student about `conditionals` and how they are used in Python. + +## Learning objectives + +- learn some general things about `control flow` in Python +- create a `conditional` structure to choose something, take a decision +- use an `if...else` structure +- use an `if..elif...else` structure + +## Out of scope + +- `ternary expressions` + +## Concepts + +- `conditionals` +- `if` +- `elif` +- `else` + +## Prerequisites + +- `basics` +- `bools` +- `comparisons` + +## Resources to refer to + +- [if statement][if statement] +- [control flow tools][control flow tools] +- [Real Python : Conditional Statements in Python][conditional statements in python] + +## Hints + +For more information on writing hints see [hints](https://github.com/exercism/docs/blob/main/anatomy/tracks/concept-exercises.md#file-docshintsmd) + + * You can refer to one or more of the resources linked , or analogous resources from a trusted source. We prefer using links within the [Python Docs](https://docs.python.org/3/) as the primary go-to, but other resources listed above are also good. Please try to avoid paid or subscription-based links if possible. + +* ### `links.json` + + For more information, see [concept links file](https://github.com/exercism/docs/blob/main/anatomy/tracks/concepts.md#file-linksjson) + + - The same resources listed can be used as a starting point for the [ `concepts/links.json`](https://github.com/exercism/docs/blob/main/anatomy/tracks/concepts.md#file-linksjson) file, if it doesn't already exist. + - If there are particularly good/interesting information sources for this concept that extend or supplement the concept exercise material & the resources already listed -- please add them to the `links.json` document. + +## Representer + +No changes needed + +## Analyzer + +No changes needed at this time. + +## Implementing + +- Code in the `.meta/examplar.py` file should **only use syntax & concepts introduced in this exercise or one of its prerequisite exercises.** + +- Please **do not use** comprehensions, generator expressions, or other syntax not previously covered. Please also follow [PEP8](https://www.python.org/dev/peps/pep-0008/) guidelines. + +- In General, tests should be written using `unittest.TestCase` and the test file should be named `_test.py` or `_test.py`. + + +- While we do use [PyTest](https://docs.pytest.org/en/stable/) as our test runner and for some implementation tests, please check with a maintainer before using a PyTest test method, fixture, or feature. + +- Our markdown and JSON files are checked against [prettier](https://prettier.io/) . We recommend [setting prettier up locally](https://prettier.io/docs/en/install.html) and running it prior to submitting your PR to avoid any CI errors. + +## Edits + +edited for updated naming by @yawpitch +edited for prerequisites and learning objectives detail by @BethanyG + +[if statement]: https://docs.python.org/3/reference/compound_stmts.html#the-if-statement +[control flow tools]: https://docs.python.org/3/tutorial/controlflow.html#more-control-flow-tools +[conditional statements in python]: https://realpython.com/python-conditional-statements/ diff --git a/test/meltdown-mitigation/.meta/exemplar.py b/test/meltdown-mitigation/.meta/exemplar.py new file mode 100644 index 0000000..c2d8ddd --- /dev/null +++ b/test/meltdown-mitigation/.meta/exemplar.py @@ -0,0 +1,84 @@ +"""Functions to prevent a nuclear meltdown.""" + + +def is_criticality_balanced(temperature, neutrons_emitted): + """Verify criticality is balanced. + + :param temperature: int or float - temperature value in kelvin. + :param neutrons_emitted: int or float - number of neutrons emitted per second. + :return: bool - is criticality balanced? + + A reactor is said to be critical if it satisfies the following conditions: + - The temperature is less than 800 K. + - The number of neutrons emitted per second is greater than 500. + - The product of temperature and neutrons emitted per second is less than 500000. + """ + + output = temperature * neutrons_emitted + balanced = False + + if (temperature < 800 and neutrons_emitted > 500) and output < 500000: + balanced = True + + return balanced + + +def reactor_efficiency(voltage, current, theoretical_max_power): + """Assess reactor efficiency zone. + + :param voltage: int or float - voltage value. + :param current: int or float - current value. + :param theoretical_max_power: int or float - power that corresponds to a 100% efficiency. + :return: str - one of ('green', 'orange', 'red', or 'black'). + + Efficiency can be grouped into 4 bands: + + 1. green -> efficiency of 80% or more, + 2. orange -> efficiency of less than 80% but at least 60%, + 3. red -> efficiency below 60%, but still 30% or more, + 4. black -> less than 30% efficient. + + The percentage value is calculated as + (generated power/ theoretical max power)*100 + where generated power = voltage * current + """ + + generated_power = voltage * current + percentage_range = (generated_power / theoretical_max_power) * 100 + + if 80 <= percentage_range <= 100: + efficiency_level = 'green' + elif 60 <= percentage_range < 80: + efficiency_level = 'orange' + elif 30 <= percentage_range < 60: + efficiency_level = 'red' + else: + efficiency_level = 'black' + + return efficiency_level + + +def fail_safe(temperature, neutrons_produced_per_second, threshold): + """Assess and return status code for the reactor. + + :param temperature: int or float - value of the temperature in kelvin. + :param neutrons_produced_per_second: int or float - neutron flux. + :param threshold: int or float - threshold for category. + :return: str - one of ('LOW', 'NORMAL', 'DANGER'). + + 1. 'LOW' -> `temperature * neutrons per second` < 90% of `threshold` + 2. 'NORMAL' -> `temperature * neutrons per second` +/- 10% of `threshold` + 3. 'DANGER' -> `temperature * neutrons per second` is not in the above-stated ranges + """ + + output = temperature * neutrons_produced_per_second + operational_percentage = (output / threshold) * 100 + + if operational_percentage < 90: + status_code = 'LOW' + elif operational_percentage <= 110: + status_code = 'NORMAL' + else: + status_code = 'DANGER' + + return status_code diff --git a/test/meltdown-mitigation/analysis.json b/test/meltdown-mitigation/analysis.json new file mode 100644 index 0000000..2e0fcfb --- /dev/null +++ b/test/meltdown-mitigation/analysis.json @@ -0,0 +1,18 @@ +{ + "summary": "There are a few suggested changes that can bring your solution closer to ideal.", + "comments": [ + { + "comment": "python.pylint.refactor", + "params": { + "lineno": "45", + "code": "R1705 no-else-return", + "message": "Unnecessary \"elif\" after \"return\", remove the leading \"el\" from \"elif", + "bad_code": "Instead of: \n```python\ndef compare_numbers(a: int, b: int) -> int:\n if a == b: # [no-else-return]\n return 0\n elif a < b:\n return -1\n else:\n return 1\n```\n\n", + "good_code": "Try: \n```python\ndef compare_numbers(a: int, b: int) -> int:\n if a == b:\n return 0\n if a < b:\n return -1\n return 1\n```\n\n", + "related_info": "- [Unnecessary-else-statements](https://www.pythonmorsels.com/unnecessary-else-statements/)\n", + "details": null + }, + "type": "actionable" + } + ] +} \ No newline at end of file diff --git a/test/meltdown-mitigation/conditionals.py b/test/meltdown-mitigation/conditionals.py new file mode 100644 index 0000000..856516c --- /dev/null +++ b/test/meltdown-mitigation/conditionals.py @@ -0,0 +1,78 @@ +"""Functions to prevent a nuclear meltdown.""" + + +def is_criticality_balanced(temperature, neutrons_emitted): + """Verify criticality is balanced. + + :param temperature: int or float - temperature value in kelvin. + :param neutrons_emitted: int or float - number of neutrons emitted per second. + :return: bool - is criticality balanced? + + A reactor is said to be critical if it satisfies the following conditions: + - The temperature is less than 800 K. + - The number of neutrons emitted per second is greater than 500. + - The product of temperature and neutrons emitted per second is less than 500000. + """ + + output = temperature * neutrons_emitted + + return (temperature < 800 and neutrons_emitted > 500 and output < 500000) + + +def reactor_efficiency(voltage, current, theoretical_max_power): + """Assess reactor efficiency zone. + + :param voltage: int or float - voltage value. + :param current: int or float - current value. + :param theoretical_max_power: int or float - power that corresponds to a 100% efficiency. + :return: str - one of ('green', 'orange', 'red', or 'black'). + + Efficiency can be grouped into 4 bands: + + 1. green -> efficiency of 80% or more, + 2. orange -> efficiency of less than 80% but at least 60%, + 3. red -> efficiency below 60%, but still 30% or more, + 4. black -> less than 30% efficient. + + The percentage value is calculated as + (generated power/ theoretical max power)*100 + where generated power = voltage * current + """ + + generated_power = voltage * current + percentage_range = (generated_power / theoretical_max_power) * 100 + + if 80 <= percentage_range <= 100: + return 'green' + elif 60 <= percentage_range < 80: + return 'orange' + elif 30 <= percentage_range < 60: + return 'red' + else: + return 'black' + + +def fail_safe(temperature, neutrons_produced_per_second, threshold): + """Assess and return status code for the reactor. + + :param temperature: int or float - value of the temperature in kelvin. + :param neutrons_produced_per_second: int or float - neutron flux. + :param threshold: int or float - threshold for category. + :return: str - one of ('LOW', 'NORMAL', 'DANGER'). + + 1. 'LOW' -> `temperature * neutrons per second` < 90% of `threshold` + 2. 'NORMAL' -> `temperature * neutrons per second` +/- 10% of `threshold` + 3. 'DANGER' -> `temperature * neutrons per second` is not in the above-stated ranges + """ + + output = temperature * neutrons_produced_per_second + operational_percentage = (output / threshold) * 100 + + if operational_percentage < 90: + status_code = 'LOW' + elif operational_percentage <= 110: + status_code = 'NORMAL' + else: + status_code = 'DANGER' + + return status_code diff --git a/test/plane-tickets/.meta/config.json b/test/plane-tickets/.meta/config.json new file mode 100644 index 0000000..1222834 --- /dev/null +++ b/test/plane-tickets/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "J08K" + ], + "contributors": [ + "BethanyG", + "kytrinyx", + "meatball133" + ], + "files": { + "solution": [ + "generators.py" + ], + "test": [ + "generators_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "new-passport", + "blurb": "Learn about generators by assigning seats to passengers on Anaconda Airlines." +} diff --git a/test/plane-tickets/.meta/design.md b/test/plane-tickets/.meta/design.md new file mode 100644 index 0000000..96a4cc3 --- /dev/null +++ b/test/plane-tickets/.meta/design.md @@ -0,0 +1,82 @@ +This issue describes how to implement the `generators` concept exercise for the Python track. + +## Goal + +The goal of this exercise is to teach the syntax and use of `generators` in Python. + +## Learning objectives + +- Understand what generators are and how/when to use them +- Understand how generators relate to `loops` and `iterators` +- Understand how to use the `yield` keyword +- Understand the `__next__()` method +- Create a generator + +## Out of scope + +- Memory and performance characteristics and optimizations +- `throw(type, value=None, traceback=None)` +- `close()` +- `generator expressions` +- `yield from` +- `generators` used as coroutines + +## Concepts covered + +- `generators` +- `yield` +- `__next__()` +- `iterators` + +## Prerequisites + +- `conditionals` +- `dicts` +- `functions` +- `higher-order-functions` +- `lists` +- `loops` +- `iteration` +- `iterators` +- `sequences` +- `classes` + +## Resources to refer to + +- [Generators (Python official docs)](https://docs.python.org/3/howto/functional.html#generators) +- [generator (Python official docs glossary)](https://docs.python.org/3/glossary.html#term-generator) +- [The yield statement (Python official docs)](https://docs.python.org/3/reference/simple_stmts.html#the-yield-statement) +- [Yield expressions (Python official docs)](https://docs.python.org/3/reference/expressions.html#yieldexpr) +- [Iterators(Python official docs)](https://docs.python.org/3/howto/functional.html?#iterators) +- [Generator-iterator methods (Python official docs)](https://docs.python.org/3/reference/expressions.html#generator-iterator-methods) +- [How to Use Generators and yield in Python (Real Python)](https://realpython.com/introduction-to-python-generators/) + +### Hints + +- Referring to one or more of the resources linked above, or analogous resources from a trusted source. +- `Generators` section of the Python Docs Functional How to tutorial: [Generators](https://docs.python.org/3/howto/functional.html#generators) + +## Concept Description + +(_a variant of this can be used for the `v3/languages/python/concepts//about.md` doc and this exercises `introduction.md` doc._) + +_**Concept Description Needs to Be Filled In Here/Written**_ + +_Some "extras" that we might want to include as notes in the concept description, or as links in `links.json`:_ + +- Additional `Generator-iterator methods`, such as `generator.send()` and `generator.throw()` +- `generator expressions` +- Asynchronous generator functions +- `generators` used as coroutines + +## Implementing + +The general Python track concept exercise implantation guide can be found [here](https://github.com/exercism/v3/blob/master/languages/python/reference/implementing-a-concept-exercise.md). + +Tests should be written using `unittest.TestCase` and the test file named `generators_test.py`. + +Code in the `.meta/example.py` file should **only use syntax & concepts introduced in this exercise or one of its prerequisites.** Please do not use comprehensions, generator expressions, or other syntax not previously covered. Please also follow [PEP8](https://www.python.org/dev/peps/pep-0008/) guidelines. + +## Help + +If you have any questions while implementing the exercise, please post the questions as comments in this issue, or contact one of the maintainers on our Slack channel. diff --git a/test/plane-tickets/.meta/exemplar.py b/test/plane-tickets/.meta/exemplar.py new file mode 100644 index 0000000..8261795 --- /dev/null +++ b/test/plane-tickets/.meta/exemplar.py @@ -0,0 +1,80 @@ +"""Functions to automate Conda airlines ticketing system.""" + +import math + +SEATS_IN_ROW = ['A', 'B', 'C', 'D'] + + +def generate_seat_letters(number): + """Generate a series of letters for airline seats. + + :param number: int - total number of seat letters to be generated. + :return: generator - generator that yields seat letters. + + Seat letters are generated from A to D. + After D it should start again with A. + + Example: A, B, C, D + + """ + + for seat in range(number): + yield SEATS_IN_ROW[seat % 4] + + +def generate_seats(number): + """Generate a series of identifiers for airline seats. + + :param number: int - total number of seats to be generated. + :return: generator - generator that yields seat numbers. + + A seat number consists of the row number and the seat letter. + + There is no row 13. + Each row has 4 seats. + + Seats should be sorted from low to high. + + Example: 3C, 3D, 4A, 4B + + """ + + number = number + 4 if number >= 13 else number + letters = generate_seat_letters(number) + for seat in range(number): + row_number = math.ceil((seat+1) / 4) + if row_number != 13: + yield f'{str(row_number)}{next(letters)}' + + # return (f'{str(row_number)}{next(letters)}' for seat in range(number) + # if (row_number := math.ceil((seat+1) / 4)) and row_number != 13) + + +def assign_seats(passengers): + """Assign seats to passengers. + + :param passengers: list[str] - a list of strings containing names of passengers. + :return: dict - with the names of the passengers as keys and seat numbers as values. + + Example output: {"Adele": "1A", "Björk": "1B"} + + """ + + number = len(passengers) + output = {} + for passenger, seat_number in zip(passengers, generate_seats(number)): + output[passenger] = seat_number + return output + +def generate_codes(seat_numbers, flight_id): + """Generate codes for a ticket. + + :param seat_numbers: list[str] - list of seat numbers. + :param flight_id: str - string containing the flight identifier. + :return: generator - generator that yields 12 character long ticket codes. + + """ + + for seat in seat_numbers: + base_string = f'{seat}{flight_id}' + yield base_string + '0' * (12 - len(base_string)) diff --git a/test/plane-tickets/analysis.json b/test/plane-tickets/analysis.json new file mode 100644 index 0000000..de41112 --- /dev/null +++ b/test/plane-tickets/analysis.json @@ -0,0 +1,18 @@ +{ + "summary": "There are a few suggested changes that can bring your solution closer to ideal.", + "comments": [ + { + "comment": "python.pylint.refactor", + "params": { + "lineno": "47", + "code": "R1708 stop-iteration-return", + "message": "Do not raise StopIteration in generator, use return statement instead", + "bad_code": "Instead of: \n```python\ndef fruit_generator():\n for fruit in [\"apple\", \"banana\"]:\n yield fruit\n raise StopIteration # [stop-iteration-return]\n\n# or\n\ndef two_fruits_generator(fruits):\n for fruit in fruits:\n yield fruit, next(fruits) # [stop-iteration-return]\n\n# or\n\ndef two_good_fruits_generator(fruits):\n for fruit in fruits:\n if not fruit.is_tasty():\n continue\n while True:\n next_fruit = next(fruits) # [stop-iteration-return]\n if next_fruit.is_tasty():\n yield fruit, next_fruit\n break\n```\n\n", + "good_code": "Try: \n```python\ndef fruit_generator():\n \"\"\"The example is simple enough you don't need an explicit return.\"\"\"\n for fruit in [\"apple\", \"banana\"]:\n yield fruit\n\n# or\n\ndef two_fruits_generator(fruits):\n \"\"\"Catching the StopIteration.\"\"\"\n for fruit in fruits:\n try:\n yield fruit, next(fruits)\n except StopIteration:\n print(\"Sorry there is only one fruit left.\")\n yield fruit, None\n\n# or\n\ndef two_good_fruits_generator(fruits):\n \"\"\"A return can be used to end the iterator early, but not a StopIteration.\"\"\"\n for fruit in fruits:\n if not fruit.is_tasty():\n continue\n while True:\n next_fruit = next(fruits, None)\n if next_fruit is None:\n print(\"Sorry there is only one fruit left.\")\n yield fruit, None\n # We reached the end of the 'fruits' generator but raising a\n # StopIteration instead of returning would create a RuntimeError\n return\n if next_fruit.is_tasty():\n yield fruit, next_fruit\n break\n```\n\n", + "related_info": "- [PEP 479](https://peps.python.org/pep-0479/)\n", + "details": "It\\'s possible to give a default value to `next` or catch the\n`StopIteration`, or return directly. A `StopIteration` cannot be\npropagated from a generator.\n" + }, + "type": "actionable" + } + ] +} \ No newline at end of file diff --git a/test/plane-tickets/generators.py b/test/plane-tickets/generators.py new file mode 100644 index 0000000..8261795 --- /dev/null +++ b/test/plane-tickets/generators.py @@ -0,0 +1,80 @@ +"""Functions to automate Conda airlines ticketing system.""" + +import math + +SEATS_IN_ROW = ['A', 'B', 'C', 'D'] + + +def generate_seat_letters(number): + """Generate a series of letters for airline seats. + + :param number: int - total number of seat letters to be generated. + :return: generator - generator that yields seat letters. + + Seat letters are generated from A to D. + After D it should start again with A. + + Example: A, B, C, D + + """ + + for seat in range(number): + yield SEATS_IN_ROW[seat % 4] + + +def generate_seats(number): + """Generate a series of identifiers for airline seats. + + :param number: int - total number of seats to be generated. + :return: generator - generator that yields seat numbers. + + A seat number consists of the row number and the seat letter. + + There is no row 13. + Each row has 4 seats. + + Seats should be sorted from low to high. + + Example: 3C, 3D, 4A, 4B + + """ + + number = number + 4 if number >= 13 else number + letters = generate_seat_letters(number) + for seat in range(number): + row_number = math.ceil((seat+1) / 4) + if row_number != 13: + yield f'{str(row_number)}{next(letters)}' + + # return (f'{str(row_number)}{next(letters)}' for seat in range(number) + # if (row_number := math.ceil((seat+1) / 4)) and row_number != 13) + + +def assign_seats(passengers): + """Assign seats to passengers. + + :param passengers: list[str] - a list of strings containing names of passengers. + :return: dict - with the names of the passengers as keys and seat numbers as values. + + Example output: {"Adele": "1A", "Björk": "1B"} + + """ + + number = len(passengers) + output = {} + for passenger, seat_number in zip(passengers, generate_seats(number)): + output[passenger] = seat_number + return output + +def generate_codes(seat_numbers, flight_id): + """Generate codes for a ticket. + + :param seat_numbers: list[str] - list of seat numbers. + :param flight_id: str - string containing the flight identifier. + :return: generator - generator that yields 12 character long ticket codes. + + """ + + for seat in seat_numbers: + base_string = f'{seat}{flight_id}' + yield base_string + '0' * (12 - len(base_string)) diff --git a/test/test_analyzer.py b/test/test_analyzer.py new file mode 100644 index 0000000..5ad3d0c --- /dev/null +++ b/test/test_analyzer.py @@ -0,0 +1,56 @@ +""" +Run tests on the analyzer itself. +""" + + +import json +import os +import subprocess +import tempfile +from pathlib import Path + +import pytest + + +ROOT = Path(__file__).parent +ANALYZER = ROOT.joinpath("..", "bin", "run.sh").resolve(strict=True) +TESTS = sorted(ROOT.glob("*/*.py")) + +def run_in_subprocess(test_path, golden_path, args=None): + """ + Run given tests against the given golden file. + """ + + exercise_dir = test_path.parent + exercise_name = exercise_dir.name + + with tempfile.TemporaryDirectory(prefix="test-analyzer-tests", dir=ROOT) as tmp_dir: + rc = subprocess.run([ANALYZER, exercise_name, exercise_dir, tmp_dir]).returncode + + analysis_json = Path(tmp_dir).joinpath("analysis.json").resolve(strict=True) + + return (json.loads(analysis_json.read_text()), json.loads(golden_path.read_text())), rc + + +@pytest.fixture(params=TESTS, ids=(os.path.split(path)[0].split("/")[-1] for path in TESTS)) +def test_with_golden(request): + """ + Path to a test and its golden files. + """ + + path = request.param + golden_analysis = path.parent.joinpath("analysis.json").resolve(strict=True) + + return (path, golden_analysis) + + +def test_results_matches_golden_file(test_with_golden): + """ + Test that the results of a run matches the golden file. + """ + + run_results, rc = run_in_subprocess(*test_with_golden) + current_run, golden = run_results + + assert current_run == golden, "results must match the golden file" + assert rc == 0, f"return code must be 0 even when errors occur: got {rc}" diff --git a/test/tisbury-treasure-hunt/.meta/config.json b/test/tisbury-treasure-hunt/.meta/config.json new file mode 100644 index 0000000..6dba773 --- /dev/null +++ b/test/tisbury-treasure-hunt/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "BethanyG" + ], + "files": { + "solution": [ + "tuples.py" + ], + "test": [ + "tuples_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "icon": "tisbury-treasure-hunt", + "blurb": "Learn about tuples by helping out competitors in the Tisbury Treasure Hunt." +} diff --git a/test/tisbury-treasure-hunt/.meta/design.md b/test/tisbury-treasure-hunt/.meta/design.md new file mode 100644 index 0000000..62b644e --- /dev/null +++ b/test/tisbury-treasure-hunt/.meta/design.md @@ -0,0 +1,38 @@ +# Design + +## Learning Objectives + +- Access values in a tuple via index using [] (bracket notation). +- Create a tuple via constructor (tuple(_iterable_)) and/or literal (a,b,c or (a, b, c)) +- Understand that tuples are an **immutable data type** (like strings). Changing a tuple means creating a new copy. +- Understand that tuples can contain other tuples. (e.g. tuples can be nested). +- Create a new tuple from two or more previous tuples via concatenation using the `+` operator. +- Iterate through a tuple using `for item in`. +- Check for membership of an item in a given tuple using the `in` keyword. +- Understand that it is the comma in a sequence that makes a tuple, and that the () are optional, except for denoting an empty tuple or when omitting them creates ambiguity. + +## Out of Scope + +- Common Sequence type methods such as `min()`, `max()`, `x.index()`, `x.count()`, `len()` +- Slicing or slice notation ([start:stop:step]) +- Additional builtin functions as they relate to this data structure (e.g. `sorted()`, `enumerate()`, `reversed()`, or `hash()`. +- Knowing that tuples can be used as objects in other data structures, -- e.g. " a "List of tuples", "tuples as keys in Dictionaries", or "A Set of tuples". +- Hash-ability and when a tuple is not hash-able. +- Related [collections](https://docs.python.org/3/library/collections.html#collections.namedtuple) module with `namedtuple()` +- Related [dataclass](https://docs.python.org/3.7/library/dataclasses.html) and `@dataclass` decorator +- Memory storage and performance characteristics. + +## Concepts + +- `tuples` + +## Prerequisites + +- `booleans` +- `for-loops` +- `functions` +- `if-keyword` +- `in-keyword` +- `integers` +- `return-keyword` +- `strings` diff --git a/test/tisbury-treasure-hunt/.meta/exemplar.py b/test/tisbury-treasure-hunt/.meta/exemplar.py new file mode 100644 index 0000000..1b4baa2 --- /dev/null +++ b/test/tisbury-treasure-hunt/.meta/exemplar.py @@ -0,0 +1,67 @@ +"""Functions to help Azara and Rui locate pirate treasure.""" + + +def get_coordinate(record): + """Return coordinate value from a tuple containing the treasure name, and treasure coordinate. + + :param record: tuple - with a (treasure, coordinate) pair. + :return: str - the extracted map coordinate. + """ + + return record[1] + + +def convert_coordinate(coordinate): + """Split the given coordinate into tuple containing its individual components. + + :param coordinate: str - a string map coordinate + :return: tuple - the string coordinate split into its individual components. + """ + + return tuple(coordinate) + + +def compare_records(azara_record, rui_record): + """Compare two record types and determine if their coordinates match. + + :param azara_record: tuple - a (treasure, coordinate) pair. + :param rui_record: tuple - a (location, tuple(coordinate_1, coordinate_2), quadrant) trio. + :return: bool - do the coordinates match? + """ + + return convert_coordinate(azara_record[1]) in rui_record + + +def create_record(azara_record, rui_record): + """Combine the two record types (if possible) and create a combined record group. + + :param azara_record: tuple - a (treasure, coordinate) pair. + :param rui_record: tuple - a (location, coordinate, quadrant) trio. + :return: tuple or str - the combined record (if compatible), or the string "not a match" (if incompatible). + """ + + result = "not a match" + + if compare_records(azara_record, rui_record): + result = azara_record + rui_record + + return result + + +def clean_up(combined_record_group): + """Clean up a combined record group into a multi-line string of single records. + + :param combined_record_group: tuple - everything from both participants. + :return: str - everything "cleaned", excess coordinates and information are removed. + + The return statement should be a multi-lined string with items separated by newlines. + + (see HINTS.md for an example). + """ + + report = "" + + for item in combined_record_group: + report += f"{(item[0], item[2], item[3], item[4])}\n" + + return report diff --git a/test/tisbury-treasure-hunt/analysis.json b/test/tisbury-treasure-hunt/analysis.json new file mode 100644 index 0000000..464bec9 --- /dev/null +++ b/test/tisbury-treasure-hunt/analysis.json @@ -0,0 +1,10 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.general.general_recommendations", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/tisbury-treasure-hunt/tuples.py b/test/tisbury-treasure-hunt/tuples.py new file mode 100644 index 0000000..1b4baa2 --- /dev/null +++ b/test/tisbury-treasure-hunt/tuples.py @@ -0,0 +1,67 @@ +"""Functions to help Azara and Rui locate pirate treasure.""" + + +def get_coordinate(record): + """Return coordinate value from a tuple containing the treasure name, and treasure coordinate. + + :param record: tuple - with a (treasure, coordinate) pair. + :return: str - the extracted map coordinate. + """ + + return record[1] + + +def convert_coordinate(coordinate): + """Split the given coordinate into tuple containing its individual components. + + :param coordinate: str - a string map coordinate + :return: tuple - the string coordinate split into its individual components. + """ + + return tuple(coordinate) + + +def compare_records(azara_record, rui_record): + """Compare two record types and determine if their coordinates match. + + :param azara_record: tuple - a (treasure, coordinate) pair. + :param rui_record: tuple - a (location, tuple(coordinate_1, coordinate_2), quadrant) trio. + :return: bool - do the coordinates match? + """ + + return convert_coordinate(azara_record[1]) in rui_record + + +def create_record(azara_record, rui_record): + """Combine the two record types (if possible) and create a combined record group. + + :param azara_record: tuple - a (treasure, coordinate) pair. + :param rui_record: tuple - a (location, coordinate, quadrant) trio. + :return: tuple or str - the combined record (if compatible), or the string "not a match" (if incompatible). + """ + + result = "not a match" + + if compare_records(azara_record, rui_record): + result = azara_record + rui_record + + return result + + +def clean_up(combined_record_group): + """Clean up a combined record group into a multi-line string of single records. + + :param combined_record_group: tuple - everything from both participants. + :return: str - everything "cleaned", excess coordinates and information are removed. + + The return statement should be a multi-lined string with items separated by newlines. + + (see HINTS.md for an example). + """ + + report = "" + + for item in combined_record_group: + report += f"{(item[0], item[2], item[3], item[4])}\n" + + return report diff --git a/test/two-fer/.meta/config.json b/test/two-fer/.meta/config.json new file mode 100644 index 0000000..b20a9a1 --- /dev/null +++ b/test/two-fer/.meta/config.json @@ -0,0 +1,30 @@ +{ + "blurb": "Create a sentence of the form \"One for X, one for me.\"", + "authors": [ + "samwincott" + ], + "contributors": [ + "cmccandless", + "denislooby", + "Dog", + "Grociu", + "ikhadykin", + "mikeyny", + "N-Parsons", + "tqa236", + "xarxziux", + "yawpitch" + ], + "files": { + "solution": [ + "two_fer.py" + ], + "test": [ + "two_fer_test.py" + ], + "example": [ + ".meta/example.py" + ] + }, + "source_url": "https://github.com/exercism/problem-specifications/issues/757" +} diff --git a/test/two-fer/.meta/example.py b/test/two-fer/.meta/example.py new file mode 100644 index 0000000..b1fd7a2 --- /dev/null +++ b/test/two-fer/.meta/example.py @@ -0,0 +1,2 @@ +def two_fer(name=None): + return "One for {}, one for me.".format(name or 'you') diff --git a/test/two-fer/.meta/template.j2 b/test/two-fer/.meta/template.j2 new file mode 100644 index 0000000..6977a3e --- /dev/null +++ b/test/two-fer/.meta/template.j2 @@ -0,0 +1,15 @@ +%- import "generator_macros.j2" as macros with context -%} +{{ macros.canonical_ref() }} +{{ macros.header() }} + +class {{ exercise | camel_case }}Test(unittest.TestCase): + {% for case in cases -%} + def test_{{ case["description"] | to_snake }}(self): + {% if case["input"]["name"] is none -%} + self.assertEqual({{ case["property"] | to_snake }}(), "{{ case["expected"] }}") + {% else -%} + self.assertEqual({{ case["property"] | to_snake }}("{{ case["input"]["name"] }}"), "{{ case["expected"] }}") + {% endif %} + {% endfor %} + +{{ macros.footer() }} diff --git a/test/two-fer/.meta/tests.toml b/test/two-fer/.meta/tests.toml new file mode 100644 index 0000000..8b1144c --- /dev/null +++ b/test/two-fer/.meta/tests.toml @@ -0,0 +1,15 @@ +# This is an auto-generated file. Regular comments will be removed when this +# file is regenerated. Regenerating will not touch any manually added keys, +# so comments can be added in a "comment" key. + +[1cf3e15a-a3d7-4a87-aeb3-ba1b43bc8dce] +description = "no name given" +include = true + +[b4c6dbb8-b4fb-42c2-bafd-10785abe7709] +description = "a name given" +include = true + +[3549048d-1a6e-4653-9a79-b0bda163e8d5] +description = "another name given" +include = true diff --git a/test/two-fer/analysis.json b/test/two-fer/analysis.json new file mode 100644 index 0000000..f2fa9fb --- /dev/null +++ b/test/two-fer/analysis.json @@ -0,0 +1,41 @@ +{ + "summary": "There are a few changes we'd like you to make before completing this exercise.", + "comments": [ + { + "comment": "python.two-fer.wrong_def_arg", + "params": {}, + "type": "essential" + }, + { + "comment": "python.two-fer.conditionals", + "params": {}, + "type": "actionable" + }, + { + "comment": "python.pylint.refactor", + "params": { + "lineno": "27", + "code": "R1705 no-else-return", + "message": "Unnecessary \"else\" after \"return\", remove the \"else\" and de-indent the code inside it", + "bad_code": "Instead of: \n```python\ndef compare_numbers(a: int, b: int) -> int:\n if a == b: # [no-else-return]\n return 0\n elif a < b:\n return -1\n else:\n return 1\n```\n\n", + "good_code": "Try: \n```python\ndef compare_numbers(a: int, b: int) -> int:\n if a == b:\n return 0\n if a < b:\n return -1\n return 1\n```\n\n", + "related_info": "- [Unnecessary-else-statements](https://www.pythonmorsels.com/unnecessary-else-statements/)\n", + "details": null + }, + "type": "actionable" + }, + { + "comment": "python.pylint.convention", + "params": { + "lineno": "27", + "code": "C0121 singleton-comparison", + "message": "Comparison 'name == None' should be 'name is None'", + "bad_code": "Instead of: \n```python\ngame_won = True\nif game_won == True: # [singleton-comparison]\n print(\"Game won !\")\n```\n\n", + "good_code": "Try: \n```python\ngame_won = True\nif game_won:\n print(\"Game won !\")\n```\n\n", + "related_info": "- [PEP 285 \u2013 Adding a bool type](https://peps.python.org/pep-0285/)\n", + "details": null + }, + "type": "actionable" + } + ] +} \ No newline at end of file diff --git a/test/two-fer/two_fer.py b/test/two-fer/two_fer.py new file mode 100644 index 0000000..d02ba7a --- /dev/null +++ b/test/two-fer/two_fer.py @@ -0,0 +1,40 @@ +''' +My solutin to two-fer +''' + +# def two_fer(name='you'): +# +# result = f"One for {name}, one for me." +# +# return result +# +# +# def two_fer(name=None): +# if name != None: +# Result = f"One for {name}, one for me." +# else: +# Result = "One for you, one for me." +# +# return Result +# + +def two_fer(name=None): + '''My solution to twofer on exercism.org. + + Uses a default argument of 'you' + ''' + + if name == None: + return "One for you, one for me." + else: + return f"One for {name}, one for me." +# +# def placeholder_0(placeholder_1=None): +# if placeholder_1 is not None: +# placeholder_2 = "One for" + placeholder_1 + ", one for me." +# #placeholder_2 = f"One for {placeholder_1}, one for me." +# else: +# placeholder_2 = "One for you, one for me." +# return placeholder_2 +# def two_fer(name='you'): +# return "One for {}, one for me.".format(name) diff --git a/test/wordy/analysis.json b/test/wordy/analysis.json new file mode 100644 index 0000000..d583583 --- /dev/null +++ b/test/wordy/analysis.json @@ -0,0 +1,83 @@ +{ + "summary": "There are a few suggested changes that can bring your solution closer to ideal.", + "comments": [ + { + "comment": "python.pylint.convention", + "params": { + "lineno": "1", + "code": "C0114 missing-module-docstring", + "message": "Missing module docstring", + "bad_code": "Instead of: \n```python\nimport sys # [missing-module-docstring]\n\n\ndef print_python_version():\n print(sys.version)\n```\n\n", + "good_code": "Try: \n```python\n\"\"\"Module providing a function printing python version.\"\"\"\n\nimport sys\n\n\ndef print_python_version():\n print(sys.version)\n```\n\n", + "related_info": null, + "details": null + }, + "type": "informative" + }, + { + "comment": "python.pylint.convention", + "params": { + "lineno": "8", + "code": "C0116 missing-function-docstring", + "message": "Missing function or method docstring", + "bad_code": "Instead of: \n```python\nimport sys\n\n\ndef print_python_version(): # [missing-function-docstring]\n print(sys.version)\n```\n\n", + "good_code": "Try: \n```python\nimport sys\n\n\ndef print_python_version():\n \"\"\"Function printing python version.\"\"\"\n print(sys.version)\n```\n\n", + "related_info": null, + "details": null + }, + "type": "informative" + }, + { + "comment": "python.pylint.refactor", + "params": { + "lineno": "9", + "code": "R1720 no-else-raise", + "message": "Unnecessary \"elif\" after \"raise\", remove the leading \"el\" from \"elif", + "bad_code": "Instead of: \n```python\ndef integer_sum(a: int, b: int) -> int:\n if not (isinstance(a, int) and isinstance(b, int)): # [no-else-raise]\n raise ValueError(\"Function supports only integer parameters.\")\n else:\n return a + b\n```\n\n", + "good_code": "Try: \n```python\ndef integer_sum(a: int, b: int) -> int:\n if not (isinstance(a, int) and isinstance(b, int)):\n raise ValueError(\"Function supports only integer parameters.\")\n return a + b\n```\n\n", + "related_info": null, + "details": null + }, + "type": "actionable" + }, + { + "comment": "python.pylint.refactor", + "params": { + "lineno": "32", + "code": "R1720 no-else-raise", + "message": "Unnecessary \"else\" after \"raise\", remove the \"else\" and de-indent the code inside it", + "bad_code": "Instead of: \n```python\ndef integer_sum(a: int, b: int) -> int:\n if not (isinstance(a, int) and isinstance(b, int)): # [no-else-raise]\n raise ValueError(\"Function supports only integer parameters.\")\n else:\n return a + b\n```\n\n", + "good_code": "Try: \n```python\ndef integer_sum(a: int, b: int) -> int:\n if not (isinstance(a, int) and isinstance(b, int)):\n raise ValueError(\"Function supports only integer parameters.\")\n return a + b\n```\n\n", + "related_info": null, + "details": null + }, + "type": "actionable" + }, + { + "comment": "python.pylint.refactor", + "params": { + "lineno": "37", + "code": "R1720 no-else-raise", + "message": "Unnecessary \"else\" after \"raise\", remove the \"else\" and de-indent the code inside it", + "bad_code": "Instead of: \n```python\ndef integer_sum(a: int, b: int) -> int:\n if not (isinstance(a, int) and isinstance(b, int)): # [no-else-raise]\n raise ValueError(\"Function supports only integer parameters.\")\n else:\n return a + b\n```\n\n", + "good_code": "Try: \n```python\ndef integer_sum(a: int, b: int) -> int:\n if not (isinstance(a, int) and isinstance(b, int)):\n raise ValueError(\"Function supports only integer parameters.\")\n return a + b\n```\n\n", + "related_info": null, + "details": null + }, + "type": "actionable" + }, + { + "comment": "python.pylint.refactor", + "params": { + "lineno": "8", + "code": "R0912 too-many-branches", + "message": "Too many branches (13/12)", + "bad_code": "Instead of: \n```python\ndef num_to_word(x): # [too-many-branches]\n if x == 0:\n return \"zero\"\n elif x == 1:\n return \"one\"\n elif x == 2:\n return \"two\"\n elif x == 3:\n return \"three\"\n elif x == 4:\n return \"four\"\n elif x == 5:\n return \"five\"\n elif x == 6:\n return \"six\"\n elif x == 7:\n return \"seven\"\n elif x == 8:\n return \"eight\"\n elif x == 9:\n return \"nine\"\n else:\n return None\n```\n\n", + "good_code": "Try: \n```python\ndef num_to_word(x):\n return {\n 0: \"zero\",\n 1: \"one\",\n 2: \"two\",\n 3: \"three\",\n 4: \"four\",\n 5: \"five\",\n 6: \"six\",\n 7: \"seven\",\n 8: \"eight\",\n 9: \"nine\",\n }.get(x)\n```\n\n", + "related_info": null, + "details": null + }, + "type": "actionable" + } + ] +} \ No newline at end of file diff --git a/test/wordy/wordy.py b/test/wordy/wordy.py new file mode 100644 index 0000000..2488153 --- /dev/null +++ b/test/wordy/wordy.py @@ -0,0 +1,49 @@ +from operator import add, mul, sub +from operator import floordiv as div + + +VALID_OPERATIONS = {'plus': add, 'minus': sub, 'multiplied by': mul, 'divided by': div} + + +def answer(question): + if not bool(question[8:-1].strip().lower().split()): + raise ValueError('syntax error') + + elif not question.startswith('What is '): + raise ValueError('unknown operation') + + else: + words = question[8:-1].strip().lower().split() + words.reverse() + + try: + main_value = int(words.pop()) + except ValueError as error: + raise ValueError('syntax error') from error + + while words: + operation = [words.pop()] + while words: + try: + next_to_evaluate = words.pop() + second_value = int(next_to_evaluate) + break + except ValueError as error: + if next_to_evaluate == operation[-1]: + raise ValueError('syntax error') from error + else: + operation.append(next_to_evaluate) + else: + if operation[-1] not in VALID_OPERATIONS and not operation[-1].isdigit() : + raise ValueError('unknown operation') + else: + raise ValueError('syntax error') + + operation = ' '.join(operation) + + try: + main_value = VALID_OPERATIONS[operation](main_value, second_value) + except KeyError as error: + raise ValueError('syntax error') from error + + return main_value diff --git a/test/yacht/.meta/config.json b/test/yacht/.meta/config.json new file mode 100644 index 0000000..352e162 --- /dev/null +++ b/test/yacht/.meta/config.json @@ -0,0 +1,25 @@ +{ + "authors": [], + "contributors": [ + "aaditkamat", + "cmccandless", + "Dog", + "Grociu", + "tqa236", + "yawpitch" + ], + "files": { + "solution": [ + "yacht.py" + ], + "test": [ + "yacht_test.py" + ], + "example": [ + ".meta/example.py" + ] + }, + "blurb": "Score a single throw of dice in the game Yacht.", + "source": "James Kilfiger, using Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Yacht_(dice_game)" +} diff --git a/test/yacht/.meta/example.py b/test/yacht/.meta/example.py new file mode 100644 index 0000000..dedcb7a --- /dev/null +++ b/test/yacht/.meta/example.py @@ -0,0 +1,65 @@ +from collections import Counter +from functools import partial + +YACHT = 0 +ONES = 1 +TWOS = 2 +THREES = 3 +FOURS = 4 +FIVES = 5 +SIXES = 6 +FULL_HOUSE = 7 +FOUR_OF_A_KIND = 8 +LITTLE_STRAIGHT = 9 +BIG_STRAIGHT = 10 +CHOICE = 11 + + +def sum_of_numbers(number, dice): + return sum(idx for idx in dice if idx == number) + + +def full_house(dice): + counter = Counter(dice) + return sum(dice) if set(counter.values()) == {3, 2} else 0 + + +def four_of_a_kind(dice): + counter = Counter(dice) + number, count = counter.most_common()[0] + return 4 * number if count >= 4 else 0 + + +def little_straight(dice): + return 30 if set(dice) == {1, 2, 3, 4, 5} else 0 + + +def big_straight(dice): + return 30 if set(dice) == {2, 3, 4, 5, 6} else 0 + + +def yacht(dice): + return 50 if len(set(dice)) == 1 else 0 + + +functions = [ + yacht, + partial(sum_of_numbers, 1), + partial(sum_of_numbers, 2), + partial(sum_of_numbers, 3), + partial(sum_of_numbers, 4), + partial(sum_of_numbers, 5), + partial(sum_of_numbers, 6), + full_house, + four_of_a_kind, + little_straight, + big_straight, + sum, +] + + +def score(dice, category): + try: + return functions[category](dice) + except IndexError as error: + raise ValueError('No such category.') from error diff --git a/test/yacht/.meta/template.j2 b/test/yacht/.meta/template.j2 new file mode 100644 index 0000000..af604a4 --- /dev/null +++ b/test/yacht/.meta/template.j2 @@ -0,0 +1,17 @@ +{%- import "generator_macros.j2" as macros with context -%} +{{ macros.canonical_ref() }} + +import unittest +import {{ exercise }} + +class {{ exercise | camel_case }}Test(unittest.TestCase): + {% for case in cases -%} + def test_{{ case["description"] | to_snake }}(self): + self.assertEqual( + {{ exercise }}.{{ case["property"] }}( + {{case["input"]["dice"]}}, + {{ exercise }}.{{ case["input"]["category"] | to_snake | upper }} + ), + {{ case["expected"] }}) + + {% endfor %} diff --git a/test/yacht/.meta/tests.toml b/test/yacht/.meta/tests.toml new file mode 100644 index 0000000..b9d9203 --- /dev/null +++ b/test/yacht/.meta/tests.toml @@ -0,0 +1,97 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[3060e4a5-4063-4deb-a380-a630b43a84b6] +description = "Yacht" + +[15026df2-f567-482f-b4d5-5297d57769d9] +description = "Not Yacht" + +[36b6af0c-ca06-4666-97de-5d31213957a4] +description = "Ones" + +[023a07c8-6c6e-44d0-bc17-efc5e1b8205a] +description = "Ones, out of order" + +[7189afac-cccd-4a74-8182-1cb1f374e496] +description = "No ones" + +[793c4292-dd14-49c4-9707-6d9c56cee725] +description = "Twos" + +[dc41bceb-d0c5-4634-a734-c01b4233a0c6] +description = "Fours" + +[f6125417-5c8a-4bca-bc5b-b4b76d0d28c8] +description = "Yacht counted as threes" + +[464fc809-96ed-46e4-acb8-d44e302e9726] +description = "Yacht of 3s counted as fives" + +[d054227f-3a71-4565-a684-5c7e621ec1e9] +description = "Fives" + +[e8a036e0-9d21-443a-8b5f-e15a9e19a761] +description = "Sixes" + +[51cb26db-6b24-49af-a9ff-12f53b252eea] +description = "Full house two small, three big" + +[1822ca9d-f235-4447-b430-2e8cfc448f0c] +description = "Full house three small, two big" + +[b208a3fc-db2e-4363-a936-9e9a71e69c07] +description = "Two pair is not a full house" + +[b90209c3-5956-445b-8a0b-0ac8b906b1c2] +description = "Four of a kind is not a full house" + +[32a3f4ee-9142-4edf-ba70-6c0f96eb4b0c] +description = "Yacht is not a full house" + +[b286084d-0568-4460-844a-ba79d71d79c6] +description = "Four of a Kind" + +[f25c0c90-5397-4732-9779-b1e9b5f612ca] +description = "Yacht can be scored as Four of a Kind" + +[9f8ef4f0-72bb-401a-a871-cbad39c9cb08] +description = "Full house is not Four of a Kind" + +[b4743c82-1eb8-4a65-98f7-33ad126905cd] +description = "Little Straight" + +[7ac08422-41bf-459c-8187-a38a12d080bc] +description = "Little Straight as Big Straight" + +[97bde8f7-9058-43ea-9de7-0bc3ed6d3002] +description = "Four in order but not a little straight" + +[cef35ff9-9c5e-4fd2-ae95-6e4af5e95a99] +description = "No pairs but not a little straight" + +[fd785ad2-c060-4e45-81c6-ea2bbb781b9d] +description = "Minimum is 1, maximum is 5, but not a little straight" + +[35bd74a6-5cf6-431a-97a3-4f713663f467] +description = "Big Straight" + +[87c67e1e-3e87-4f3a-a9b1-62927822b250] +description = "Big Straight as little straight" + +[c1fa0a3a-40ba-4153-a42d-32bc34d2521e] +description = "No pairs but not a big straight" + +[207e7300-5d10-43e5-afdd-213e3ac8827d] +description = "Choice" + +[b524c0cf-32d2-4b40-8fb3-be3500f3f135] +description = "Yacht as choice" diff --git a/test/yacht/analysis.json b/test/yacht/analysis.json new file mode 100644 index 0000000..3710881 --- /dev/null +++ b/test/yacht/analysis.json @@ -0,0 +1,18 @@ +{ + "summary": "Good work! \ud83c\udf1f Here are some general recommendations for improving your Python code.", + "comments": [ + { + "comment": "python.pylint.convention", + "params": { + "lineno": "1", + "code": "C0114 missing-module-docstring", + "message": "Missing module docstring", + "bad_code": "Instead of: \n```python\nimport sys # [missing-module-docstring]\n\n\ndef print_python_version():\n print(sys.version)\n```\n\n", + "good_code": "Try: \n```python\n\"\"\"Module providing a function printing python version.\"\"\"\n\nimport sys\n\n\ndef print_python_version():\n print(sys.version)\n```\n\n", + "related_info": null, + "details": null + }, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/test/yacht/yacht.py b/test/yacht/yacht.py new file mode 100644 index 0000000..dedcb7a --- /dev/null +++ b/test/yacht/yacht.py @@ -0,0 +1,65 @@ +from collections import Counter +from functools import partial + +YACHT = 0 +ONES = 1 +TWOS = 2 +THREES = 3 +FOURS = 4 +FIVES = 5 +SIXES = 6 +FULL_HOUSE = 7 +FOUR_OF_A_KIND = 8 +LITTLE_STRAIGHT = 9 +BIG_STRAIGHT = 10 +CHOICE = 11 + + +def sum_of_numbers(number, dice): + return sum(idx for idx in dice if idx == number) + + +def full_house(dice): + counter = Counter(dice) + return sum(dice) if set(counter.values()) == {3, 2} else 0 + + +def four_of_a_kind(dice): + counter = Counter(dice) + number, count = counter.most_common()[0] + return 4 * number if count >= 4 else 0 + + +def little_straight(dice): + return 30 if set(dice) == {1, 2, 3, 4, 5} else 0 + + +def big_straight(dice): + return 30 if set(dice) == {2, 3, 4, 5, 6} else 0 + + +def yacht(dice): + return 50 if len(set(dice)) == 1 else 0 + + +functions = [ + yacht, + partial(sum_of_numbers, 1), + partial(sum_of_numbers, 2), + partial(sum_of_numbers, 3), + partial(sum_of_numbers, 4), + partial(sum_of_numbers, 5), + partial(sum_of_numbers, 6), + full_house, + four_of_a_kind, + little_straight, + big_straight, + sum, +] + + +def score(dice, category): + try: + return functions[category](dice) + except IndexError as error: + raise ValueError('No such category.') from error