diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..c8ebd8c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,41 @@ +name: halo + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + test: + name: Test with ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + python-version: + - "3.8" + - "3.9" + - "3.10" + - "3.11" + os: + - ubuntu-latest + - macos-latest + - windows-latest + + steps: + - uses: actions/checkout@v4 + - name: Setup python for test ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python --version + python -m pip install --upgrade pip setuptools wheel + pip --version + python -m pip install --upgrade tox tox-gh>=1.2 + tox --version + - name: Run tox targets for ${{ matrix.python-version }} + run: | + python -m tox diff --git a/.gitignore b/.gitignore index 22a596f..a5bed11 100644 --- a/.gitignore +++ b/.gitignore @@ -105,6 +105,7 @@ cover/ # tests tests/*.txt +.pytest_cache # OS-specific files .DS_Store diff --git a/.pylintrc b/.pylintrc index 9790dac..720692f 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,294 +1,370 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore= - -# Add files or directories matching the regex patterns to the blacklist. The -# regex matches against base names, not paths. -ignore-patterns= +[MAIN] # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). -init-hook= - import sys, os - - if 'VIRTUAL_ENV' not in os.environ: \ - sys.exit(0) +#init-hook= - ve_dir = os.environ['VIRTUAL_ENV'] - ve_dir in sys.path or sys.path.insert(0, ve_dir) - activate_this = os.path.join(os.path.join(ve_dir, 'bin'), 'activate_this.py') +# Files or directories to be skipped. They should be base names, not +# paths. +ignore=CVS - # Fix for windows - if not os.path.exists(activate_this): \ - activate_this = os.path.join(os.path.join(ve_dir, 'Scripts'), 'activate_this.py') +# Add files or directories matching the regex patterns to the ignore-list. The +# regex matches against paths and can be in Posix or Windows format. +ignore-paths= - with open(activate_this) as fp: \ - exec(fp.read(), dict(__file__=activate_this)) +# Files or directories matching the regex patterns are skipped. The regex +# matches against base names, not paths. +ignore-patterns=^\.# -# Use multiple processes to speed up Pylint. -jobs=1 +# Pickle collected data for later comparisons. +persistent=yes # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. load-plugins= + pylint.extensions.check_elif, + pylint.extensions.bad_builtin, + pylint.extensions.docparams, + pylint.extensions.for_any_all, + pylint.extensions.set_membership, + pylint.extensions.code_style, + pylint.extensions.overlapping_exceptions, + pylint.extensions.typing, + pylint.extensions.redefined_variable_type, + pylint.extensions.comparison_placement, + pylint.extensions.broad_try_clause, + pylint.extensions.dict_init_mutate, + pylint.extensions.consider_refactoring_into_while_condition, + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use. +jobs=1 -# Pickle collected data for later comparisons. -persistent=yes - -# Specify a configuration file. -#rcfile= +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-allow-list= + +# Minimum supported python version +py-version = 3.8.0 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# Specify a score threshold under which the program will exit with error. +fail-under=10.0 + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +fail-on= + +# Clear in-memory caches upon conclusion of linting. Useful if running pylint in +# a server-like mode. +clear-cache-post-run=no + [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= +# 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= + use-symbolic-message-instead, + useless-suppression, # 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 reenable specific checks. For example, if +# 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" -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call -# 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= + attribute-defined-outside-init, + invalid-name, + missing-docstring, + protected-access, + too-few-public-methods, + # handled by black + format, + # We anticipate #3512 where it will become optional + fixme, + consider-using-assignment-expr, [REPORTS] -# 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= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio).You can also give a reporter class, eg +# 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=text # Tells whether to display a full report or only the messages reports=no -# Activate the evaluation score. -score=yes +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables 'fatal', 'error', 'warning', 'refactor', 'convention' +# and 'info', which contain the number of messages in each category, as +# well as 'statement', which is the total number of statements analyzed. This +# score is used by the global evaluation report (RP0004). +evaluation=max(0, 0 if fatal else 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= -[REFACTORING] +# Activate the evaluation score. +score=yes -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 +[LOGGING] -[BASIC] +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging -# Naming hint for argument names -argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old -# Regular expression matching correct argument names -argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Naming hint for attribute names -attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ +[MISCELLANEOUS] -# Regular expression matching correct attribute names -attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata +# Regular expression of note tags to take in consideration. +#notes-rgx= -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ +[SIMILARITIES] -# Naming hint for class names -class-name-hint=[A-Z_][a-zA-Z0-9]+$ +# Minimum lines number of a similarity. +min-similarity-lines=6 -# Regular expression matching correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ +# Ignore comments when computing similarities. +ignore-comments=yes -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ +# Ignore docstrings when computing similarities. +ignore-docstrings=yes -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ +# Ignore imports when computing similarities. +ignore-imports=yes -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 +# Signatures are removed from the similarity computation +ignore-signatures=yes -# Naming hint for function names -function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Regular expression matching correct function names -function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ +[VARIABLES] -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_ +# Tells whether we should check for unused import in __init__ files. +init-import=no -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes -# Naming hint for method names -method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ +# List of names allowed to shadow builtins +allowed-redefined-builtins= -# Regular expression matching correct method names -method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ +[FORMAT] -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= +# Maximum number of characters on a single line. +max-line-length=100 -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ -# 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 +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no -# Naming hint for variable names -variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no -# Regular expression matching correct variable names -variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ +# Maximum number of lines in a module +max-module-lines=2000 +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' -[FORMAT] +# 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= -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 +[BASIC] -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ -# Maximum number of characters on a single line. -max-line-length=100 +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= -# Maximum number of lines in a module -max-module-lines=1000 +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata -# 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. -no-space-check=trailing-comma,dict-separator +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no +# Naming style matching correct function names. +function-naming-style=snake_case -[LOGGING] +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging +# Naming style matching correct variable names. +variable-naming-style=snake_case +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ -[MISCELLANEOUS] +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ +# Naming style matching correct attribute names. +attr-naming-style=snake_case -[SIMILARITIES] +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,}$ -# Ignore comments when computing similarities. -ignore-comments=yes +# Naming style matching correct argument names. +argument-naming-style=snake_case -# Ignore docstrings when computing similarities. -ignore-docstrings=yes +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ -# Ignore imports when computing similarities. -ignore-imports=no +# Naming style matching correct class attribute names. +class-attribute-naming-style=any -# Minimum lines number of a similarity. -min-similarity-lines=4 +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE -[SPELLING] +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. +#class-const-rgx= -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any -# List of comma separated words that should not be checked. -spelling-ignore-words= +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ -# 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 + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,}$ + +# Regular expression matching correct type variable names +#typevar-rgx= + +# Regular expression which should only match function or class names that do +# not require a docstring. Use ^(?!__init__$)_ to also check __init__. +no-docstring-rgx=__.*__ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# List of decorators that define properties, such as abc.abstractproperty. +property-classes=abc.abstractproperty [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 +# Regex pattern to define which classes are considered mixins if ignore-mixin- +# members is set to 'yes' +mixin-class-rgx=.*MixIn + +# List of module names for which member attributes should not be checked and +# will not be imported (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=SQLObject, 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= +generated-members=REQUEST,acl_users,aq_parent,argparse.Namespace + +# List of decorators that create context managers from functions, such as +# contextlib.contextmanager. +contextmanager-decorators=contextlib.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 +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes # This flag controls whether pylint should warn about no-member and similar # checks whenever an opaque object is returned when inferring. The inference @@ -298,17 +374,6 @@ ignore-mixin-members=yes # the rest of the inferred objects. ignore-on-opaque-inference=yes -# 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,Enum - -# 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=setup - # 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 @@ -321,90 +386,83 @@ missing-member-hint-distance=1 # showing a hint for a missing member. missing-member-max-choices=1 +[SPELLING] -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,future.builtins - +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= -[CLASSES] +# List of comma separated words that should not be checked. +spelling-ignore-words= -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp +# List of comma separated words that should be considered directives if they +# appear and the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:,pragma:,# noinspection -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file=.pyenchant_pylint_custom_dict.txt -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls +# 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 -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=2 [DESIGN] # Maximum number of arguments for function / method -max-args=5 +max-args = 9 -# Maximum number of attributes for a class (see R0902). -max-attributes=7 +# Maximum number of locals for function / method body +max-locals = 19 -# Maximum number of boolean expressions in a if statement -max-bool-expr=5 +# Maximum number of return / yield for function / method body +max-returns=11 # Maximum number of branch for function / method body -max-branches=12 +max-branches = 20 -# Maximum number of locals for function / method body -max-locals=15 +# Maximum number of statements in function / method body +max-statements = 50 -# Maximum number of parents for a class (see R0901). -max-parents=7 +# Maximum number of attributes for a class (see R0902). +max-attributes=11 -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 +# Maximum number of statements in a try-block +max-try-statements = 7 -# Maximum number of return / yield for function / method body -max-returns=6 +[CLASSES] -# Maximum number of statements in function / method body -max-statements=50 +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp,__post_init__ + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no [IMPORTS] +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + # Allow wildcard imports from modules that define __all__. allow-wildcard-with-all=no +# Allow explicit reexports by alias from a package __init__. +allow-reexport-from-package=no + # 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. @@ -413,28 +471,79 @@ analyse-fallback-blocks=no # Deprecated modules which should not be used, separated by a comma deprecated-modules=regsub,TERMIOS,Bastion,rexec -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - # 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= +known-standard-library=_string # Force import order to recognize a module as part of a third party library. known-third-party=enchant +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + [EXCEPTIONS] # Exceptions that will emit a warning when being caught. Defaults to # "Exception" -overgeneral-exceptions=Exception +overgeneral-exceptions=builtins.Exception + + +[TYPING] + +# Set to ``no`` if the app / library does **NOT** need to support runtime +# introspection of type annotations. If you use type annotations +# **exclusively** for type checking of an application, you're probably fine. +# For libraries, evaluate if some users what to access the type hints at +# runtime first, e.g., through ``typing.get_type_hints``. Applies to Python +# versions 3.7 - 3.9 +runtime-typing = no + + +[DEPRECATED_BUILTINS] + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,input + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# 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 + + +[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=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[CODE_STYLE] + +# Max line length for which to sill emit suggestions. Used to prevent optional +# suggestions which would get split by a code formatter (e.g., black). Will +# default to the setting for ``max-line-length``. +#max-line-length-suggestions= \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f92eecf..0000000 --- a/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: python -cache: pip -git: - depth: 5 -matrix: - include: - - python: '3.5' - env: TOXENV=lint - - python: '3.5' - env: TOXENV=py35 - - python: '3.6' - env: TOXENV=py36 - - python: '3.7' - dist: xenial - sudo: true - env: TOXENV=py37 - - python: '3.8' - dist: xenial - sudo: true - env: TOXENV=py38 - fast_finish: true -install: - - pip install tox coveralls -script: - - tox -e $TOXENV -after_success: - - coveralls diff --git a/README.md b/README.md index 5eb463c..6c22245 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ halo -[![Build Status](https://travis-ci.org/manrajgrover/halo.svg?branch=master)](https://travis-ci.org/manrajgrover/halo) [![Build status](https://ci.appveyor.com/api/projects/status/wa6t414gltr403ff?svg=true)](https://ci.appveyor.com/project/manrajgrover/halo) [![Coverage Status](https://coveralls.io/repos/github/manrajgrover/halo/badge.svg?branch=master)](https://coveralls.io/github/manrajgrover/halo?branch=master) +[![Build Status](https://travis-ci.com/manrajgrover/halo.svg?branch=master)](https://travis-ci.com/manrajgrover/halo) [![Build status](https://ci.appveyor.com/api/projects/status/wa6t414gltr403ff?svg=true)](https://ci.appveyor.com/project/manrajgrover/halo) [![Coverage Status](https://coveralls.io/repos/github/manrajgrover/halo/badge.svg?branch=master)](https://coveralls.io/github/manrajgrover/halo?branch=master) [![PyPI](https://img.shields.io/pypi/v/halo.svg)](https://github.com/manrajgrover/halo) ![awesome](https://img.shields.io/badge/awesome-yes-green.svg) [![Downloads](https://pepy.tech/badge/halo)](https://pepy.tech/project/halo) [![Downloads](https://pepy.tech/badge/halo/month)](https://pepy.tech/project/halo/month) > Beautiful spinners for terminal, IPython and Jupyter diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 855678f..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,55 +0,0 @@ -init: - - ps: echo $env:TOXENV - -cache: - - '%LOCALAPPDATA%\pip\Cache' - -clone_depth: 5 - -environment: - fast_finish: true - matrix: - - - TOXENV: 'py35' - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - - - TOXENV: 'py36' - TOXPYTHON: C:\Python36\python.exe - PYTHON_HOME: C:\Python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - - - TOXENV: 'py37' - TOXPYTHON: C:\Python37\python.exe - PYTHON_HOME: C:\Python37 - PYTHON_VERSION: '3.7' - PYTHON_ARCH: '32' - - - TOXENV: 'py38' - TOXPYTHON: C:\Python38\python.exe - PYTHON_HOME: C:\Python38 - PYTHON_VERSION: '3.8' - PYTHON_ARCH: '32' - - - TOXENV: 'lint' - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - -matrix: - fast_finish: true - -install: - - '%PYTHON_HOME%\Scripts\pip --version' - - '%PYTHON_HOME%\Scripts\pip install tox' - - '%PYTHON_HOME%\Scripts\tox --version' - - '%PYTHON_HOME%\Scripts\virtualenv --version' - -build: off - -test_script: - - '%PYTHON_HOME%\Scripts\tox -e %TOXENV%' diff --git a/halo/_utils.py b/halo/_utils.py index 2f5b169..2e7c5e4 100644 --- a/halo/_utils.py +++ b/halo/_utils.py @@ -5,11 +5,9 @@ import platform import six import sys -try: - from shutil import get_terminal_size -except ImportError: - from backports.shutil_get_terminal_size import get_terminal_size +from colorama import init +from shutil import get_terminal_size from termcolor import colored diff --git a/halo/halo.py b/halo/halo.py index f57ff59..c8368e7 100644 --- a/halo/halo.py +++ b/halo/halo.py @@ -102,7 +102,7 @@ def __init__( environment = get_environment() - def clean_up(): + def clean_up(args): """Handle cell execution""" self.stop() @@ -497,7 +497,7 @@ def start(self, text=None): self._stop_spinner = threading.Event() self._spinner_thread = threading.Thread(target=self.render) - self._spinner_thread.setDaemon(True) + self._spinner_thread.daemon = True self._render_frame() self._spinner_id = self._spinner_thread.name self._spinner_thread.start() diff --git a/halo/halo_notebook.py b/halo/halo_notebook.py index 753fd7a..3bf097b 100644 --- a/halo/halo_notebook.py +++ b/halo/halo_notebook.py @@ -53,6 +53,7 @@ def clear(self): self.output.outputs += self._output(self.CLEAR_LINE) self.output.outputs = self._output() + self.output.close() return self def _render_frame(self): @@ -77,7 +78,7 @@ def start(self, text=None): display(self.output) self._stop_spinner = threading.Event() self._spinner_thread = threading.Thread(target=self.render) - self._spinner_thread.setDaemon(True) + self._spinner_thread.daemon = True self._render_frame() self._spinner_id = self._spinner_thread.name self._spinner_thread.start() diff --git a/requirements-dev.txt b/requirements-dev.txt index 22a29e1..f199d47 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ coverage>=4.4.1 -nose>=1.3.7 +pytest>=8.2.2 pylint>=1.7.2 -tox>=2.8.2 +tox>=4 twine>=1.12.1 diff --git a/requirements.txt b/requirements.txt index 0cc3671..2c7c323 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -backports.shutil_get_terminal_size>=1.0.0;python_version < '3.3' log_symbols>=0.0.14 spinners>=0.0.24 termcolor>=1.1.0 diff --git a/setup.py b/setup.py index 3de31cc..b95f471 100644 --- a/setup.py +++ b/setup.py @@ -15,9 +15,18 @@ def dependencies(file): setup( name="halo", - packages=find_packages(exclude=("tests", "examples")), - version="0.0.30", + packages=find_packages(exclude=("tests", "examples", "art")), + version="0.0.31", license="MIT", + classifiers=[ + "Programming Language :: Python", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3 :: Only", + ], + python_requires=">=3.4", description="Beautiful terminal spinners in Python", long_description=long_description, long_description_content_type="text/markdown", @@ -41,5 +50,10 @@ def dependencies(file): install_requires=dependencies("requirements.txt"), tests_require=dependencies("requirements-dev.txt"), include_package_data=True, - extras_require={"ipython": ["IPython==5.7.0", "ipywidgets==7.1.0",]}, + extras_require={ + "ipython": [ + "IPython>=8.12.3", + "ipywidgets>=8.1.3", + ] + }, ) diff --git a/tests/test_halo.py b/tests/test_halo.py index 6f204b2..b83f5c4 100644 --- a/tests/test_halo.py +++ b/tests/test_halo.py @@ -158,7 +158,7 @@ def test_text_stripping(self): pattern = re.compile(r'(✔|v) foo', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) def test_text_ellipsing(self): """Test the text gets ellipsed if it's too long @@ -182,7 +182,7 @@ def test_text_ellipsing(self): pattern = re.compile(r'(✔|v) End!', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) def test_text_animation(self): """Test the text gets animated when it is too long @@ -205,7 +205,7 @@ def test_text_animation(self): pattern = re.compile(r'(✔|v) End!', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) def test_context_manager(self): """Test the basic of basic spinners used through the with statement. @@ -296,7 +296,7 @@ def test_succeed(self): output = self._get_test_output()['text'] pattern = re.compile(r'(✔|v) foo', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) spinner.stop() def test_succeed_with_new_text(self): @@ -309,7 +309,7 @@ def test_succeed_with_new_text(self): output = self._get_test_output()['text'] pattern = re.compile(r'(✔|v) bar', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) spinner.stop() def test_info(self): @@ -322,7 +322,7 @@ def test_info(self): output = self._get_test_output()['text'] pattern = re.compile(r'(ℹ|¡) foo', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) spinner.stop() def test_fail(self): @@ -335,7 +335,7 @@ def test_fail(self): output = self._get_test_output()['text'] pattern = re.compile(r'(✖|×) foo', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) spinner.stop() def test_warning(self): @@ -348,7 +348,7 @@ def test_warning(self): output = self._get_test_output()['text'] pattern = re.compile(r'(⚠|!!) Warning!', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) spinner.stop() def test_spinner_getters_setters(self): @@ -543,7 +543,7 @@ def test_right_placement(self): pattern = re.compile(r"(✔|v)", re.UNICODE) self.assertEqual(text, 'foo') - self.assertRegexpMatches(symbol, pattern) + self.assertRegex(symbol, pattern) spinner.stop() def test_bounce_animation(self): @@ -582,14 +582,14 @@ def filler_text(n_chars): zipped_expected_and_actual_frame = zip(expected_frames, output) for multiple_frames in zipped_expected_and_actual_frame: expected_frame, actual_frame = multiple_frames - self.assertEquals(expected_frame, actual_frame) + self.assertEqual(expected_frame, actual_frame) def test_animation_setter(self): spinner = Halo("Asdf") spinner.animation = "bounce" - self.assertEquals("bounce", spinner.animation) + self.assertEqual("bounce", spinner.animation) spinner.animation = "marquee" - self.assertEquals("marquee", spinner.animation) + self.assertEqual("marquee", spinner.animation) @unittest.skipIf(sys.platform.startswith("win"), "ANSI not supported on Windows") def test_spinner_color(self): @@ -605,7 +605,7 @@ def test_spinner_color(self): output = self._get_test_output(no_ansi=False) output_merged = [arr for c in output['colors'] for arr in c] - self.assertEquals(str(color_int) in output_merged, True) + self.assertEqual(str(color_int) in output_merged, True) def test_redirect_stdout(self): """Test redirect stdout diff --git a/tests/test_halo_notebook.py b/tests/test_halo_notebook.py index b6d55a9..69c3330 100644 --- a/tests/test_halo_notebook.py +++ b/tests/test_halo_notebook.py @@ -126,7 +126,7 @@ def test_text_stripping(self): pattern = re.compile(r'(✔|v) foo', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) def test_text_ellipsing(self): """Test the text gets ellipsed if it's too long @@ -152,7 +152,7 @@ def test_text_ellipsing(self): pattern = re.compile(r'(✔|v) End!', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) def test_text_animation(self): """Test the text gets animated when it is too long @@ -177,7 +177,7 @@ def test_text_animation(self): pattern = re.compile(r'(✔|v) End!', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) def test_context_manager(self): """Test the basic of basic spinners used through the with statement. @@ -257,7 +257,7 @@ def test_succeed(self): output = self._get_test_output(spinner)['text'] pattern = re.compile(r'(✔|v) foo', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) spinner.stop() def test_succeed_with_new_text(self): @@ -270,7 +270,7 @@ def test_succeed_with_new_text(self): output = self._get_test_output(spinner)['text'] pattern = re.compile(r'(✔|v) bar', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) spinner.stop() def test_info(self): @@ -283,7 +283,7 @@ def test_info(self): output = self._get_test_output(spinner)['text'] pattern = re.compile(r'(ℹ|¡) foo', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) spinner.stop() def test_fail(self): @@ -296,7 +296,7 @@ def test_fail(self): output = self._get_test_output(spinner)['text'] pattern = re.compile(r'(✖|×) foo', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) spinner.stop() def test_warning(self): @@ -309,7 +309,7 @@ def test_warning(self): output = self._get_test_output(spinner)['text'] pattern = re.compile(r'(⚠|!!) Warning!', re.UNICODE) - self.assertRegexpMatches(output[-1], pattern) + self.assertRegex(output[-1], pattern) spinner.stop() def test_spinner_getters_setters(self): @@ -407,7 +407,7 @@ def test_right_placement(self): pattern = re.compile(r"(✔|v)", re.UNICODE) self.assertEqual(text, "foo") - self.assertRegexpMatches(symbol, pattern) + self.assertRegex(symbol, pattern) spinner.stop() def test_spinner_color(self): @@ -422,7 +422,7 @@ def test_spinner_color(self): output_merged = [arr for c in output['colors'] for arr in c] - self.assertEquals(str(color_int) in output_merged, True) + self.assertEqual(str(color_int) in output_merged, True) def tearDown(self): """Clean up things after every test. diff --git a/tox.ini b/tox.ini index 7b63e7f..ecd54bb 100644 --- a/tox.ini +++ b/tox.ini @@ -1,14 +1,17 @@ [tox] envlist = - py34, - py35, - py36, - py37, - py38, + py{38,39,310,311}-{linux,macos,windows}, lint skip_missing_interpreters = True +[gh] +python = + 3.8 = py38, lint + 3.9 = py39 + 3.10 = py310 + 3.11 = py311 + [testenv] # See https://github.com/pytest-dev/pytest/pull/5222#issuecomment-492428610 download = True @@ -22,7 +25,7 @@ setenv = LANG=en_US.UTF-8 commands = - nosetests --cover-package=halo --with-coverage --cover-erase --cover-branches --nologcapture + pytest [testenv:lint] commands =