From 4468e427de56f5aa058d43e92109058869b9edeb Mon Sep 17 00:00:00 2001 From: James Ridgway Date: Sat, 5 Mar 2022 14:47:44 +0000 Subject: [PATCH 1/4] Initial linting and testing improvements --- .coverage | Bin 0 -> 53248 bytes .github/workflows/ci.yml | 23 + .gitignore | 11 +- .pylintrc | 598 ++++++++++++++++++++++++++ attachment_downloader/__init__.py | 1 - attachment_downloader/cli.py | 5 +- attachment_downloader/logging.py | 6 +- attachment_downloader/version_info.py | 16 +- requirements.txt | 5 +- run-pylint.sh | 2 + run-tests.sh | 2 + setup.sh | 6 + tests/__init__.py | 0 13 files changed, 654 insertions(+), 21 deletions(-) create mode 100644 .coverage create mode 100644 .github/workflows/ci.yml create mode 100644 .pylintrc create mode 100755 run-pylint.sh create mode 100755 run-tests.sh create mode 100755 setup.sh create mode 100644 tests/__init__.py diff --git a/.coverage b/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..138200889572f31cbc9875c9d3d0464b8b9fac57 GIT binary patch literal 53248 zcmeI*%WoS+90%}SuN}vEZBZ3jC8{z~Rk=~)IFA-Y5h?+4s9LG72o)jNuI+KW!Mp43 zj`KJmudnLR@;`_uEI-PUEU-DpifXt9ZSSnc11oyu4XA zy?gtHqoip0fom(Vrk&DsU3*^$P18#BnxA1Z1|MVJr$QW@ZFm7MaS`?VtS#Y9N!aiPd0i=HjgaeBBP}X;SH9C(z?$Dx*x|( zt~H>OMLr^C5UqwCo;;}hMUll^jEdp;i-HS5E#Um*SoxjPx<;*1?u8FJ^uf!r zd&-aYEvkHLg;rBvzcF4c&(G_RA4Cm>d91pl!h?;{P@_3GJfi9F`O)o$d&zJk;a=3~ z2)`9|8ICRD%AwXg*k@`fh8=K66Mhnqa{5YJ8)ugCAnF~zKHS!#786S9AT$=F!CF19 z!CfWfGBo@h8Q5ELDLESt3$MB%QPWu|jg>d%M>U;;CSa``Y)D97o-Epvj)jDMm9>OeIz+U#iGcu>wZr~?U#3>=E-wU zmftvmY(KT9PzzCT>fUsr@JhNPDs4OD!6&SUy6<72r&`zH0Zv|G`J>)Kv3nWlL5jZ>u|pZg?VCmuT}qNq?`45?=xN;6F~ zIm-i8n*LIkC^%KuZpOWw>tZ0q-L`xxxp)%#$W~H0t`r<3%1qRrcI{9!0!b%n3SFoe z&#HZ{Bx{A&e3v}VA7uLTda-=*qTcT%eJ~zgtdQn%*G{vY?B<5r#-Wp0Gr39QEPcos zY#wYkz`?um6?jaGYp@wegRh%UH2T2;0SG_<0uX=z1Rwwb2tWV=5P-nR z6EO6gUgZ0K!|ZD2_vSbBh6Mr;fB*y_009U<00Izz00bZaftMigreVyk@plYXmseM- z%WKu;%VOoyl}pQ4E?+it##}jhLjTs%+S016=g;=vPQP36X=SOlBfXtk-SKMOeI-Lx zSrnCOmA)!a4P@63qW!jVI|s+#vz4+NZC85AYWllg$ER;b1bmvKhn3GJs7!|Whi3k3 z{zY$CAOHafKmY;|fB*y_009U<00Izr{RGC1S)G5|U`!fwdh)A<+@x`~l>SnIpZ^zT z9L@a6yk{0?exXe)5P$##AOHafKmY;|fB*y_0D%`5xLL?+{d2Xp@531R9e8qG>-bw+^apY1U;1CqQ-X8HPO#B&D6yNyOUYewhfN9I%29&n zkDWWqay&;_7NxkUQ;OANr7;YPkccl-q7`~UpQ$z68;Z`{uJ&mTW`1NZ;A z&3wOFIyT^;`~T<<{GUSmga8B}009U<00Izz00bZa0SG|gBn$BKfBH}Vus{F;5P$## zAOHafKmY;|fB*y_aDoN!{Qm@3E{X;L2tWV=5P$##AOHafKmY;|NCfcwA58%Q5P$## zAOHafKmY;|fB*y_aPkH4{Qu?$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=120 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# 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 + +# 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 + +# 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 + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +#notes-rgx= + + +[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 + +# 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= + +# 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 +# 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 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 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= + +# 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 + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# 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 + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining 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. expected to +# not be 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,past.builtins,future.builtins,builtins,io + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it work, +# install the 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 the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[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 + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. +#class-attribute-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. +#variable-rgx= + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + __post_init__ + +# 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 + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[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 + +# 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 + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules=optparse,tkinter.tix + +# 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 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 + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[DESIGN] + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=1 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "BaseException, Exception". +overgeneral-exceptions=BaseException, + Exception diff --git a/attachment_downloader/__init__.py b/attachment_downloader/__init__.py index c698bc3..e69de29 100644 --- a/attachment_downloader/__init__.py +++ b/attachment_downloader/__init__.py @@ -1 +0,0 @@ -import attachment_downloader \ No newline at end of file diff --git a/attachment_downloader/cli.py b/attachment_downloader/cli.py index aee4a7a..973b236 100644 --- a/attachment_downloader/cli.py +++ b/attachment_downloader/cli.py @@ -11,11 +11,10 @@ def valid_date(option, opt, value): parsed_value = parsed_value.replace(tzinfo=datetime.timezone.utc) return parsed_value except ValueError: - raise OptionValueError('option %s: invalid date format: %r' % (opt, value)) + raise OptionValueError(f'option {opt}: invalid date format: {value}') def get_password(): if sys.stdin.isatty(): return getpass('IMAP Password: ') - else: - return sys.stdin.read().strip() + return sys.stdin.read().strip() diff --git a/attachment_downloader/logging.py b/attachment_downloader/logging.py index 6b0c55d..db0446e 100644 --- a/attachment_downloader/logging.py +++ b/attachment_downloader/logging.py @@ -3,8 +3,8 @@ class InfoFilter(logging.Filter): - def filter(self, rec): - return rec.levelno in (logging.DEBUG, logging.INFO) + def filter(self, record): + return record.levelno in (logging.DEBUG, logging.INFO) class Logger: @staticmethod @@ -21,4 +21,4 @@ def setup(): root_logger = logging.getLogger() root_logger.setLevel(logging.INFO) root_logger.addHandler(std_out_stream_handler) - root_logger.addHandler(std_err_stream_handler) \ No newline at end of file + root_logger.addHandler(std_err_stream_handler) diff --git a/attachment_downloader/version_info.py b/attachment_downloader/version_info.py index ba3d4cf..20ff3b8 100644 --- a/attachment_downloader/version_info.py +++ b/attachment_downloader/version_info.py @@ -1,8 +1,8 @@ import os import platform +import subprocess VERSION_FILENAME = os.path.abspath(os.path.join(os.path.dirname(__file__), 'version.py')) -import subprocess class Version: @@ -10,9 +10,9 @@ class Version: def generate(): process = subprocess.Popen(["git", "describe", "--always", "--tags"], stdout=subprocess.PIPE, stderr=None) last_tag = process.communicate()[0].decode('ascii').strip() - version = last_tag.split('-g')[0].replace('-', '.') if '-g' in last_tag else last_tag - with open(VERSION_FILENAME, 'w') as f: - f.write(f'ATTACHMENT_DOWNLOADER_VERSION = "{version}"\n') + version = last_tag.split('-g', maxsplit=1)[0].replace('-', '.') if '-g' in last_tag else last_tag + with open(VERSION_FILENAME, 'w') as file: + file.write(f'ATTACHMENT_DOWNLOADER_VERSION = "{version}"\n') return version @staticmethod @@ -20,12 +20,12 @@ def get(retry=True): try: from attachment_downloader.version import ATTACHMENT_DOWNLOADER_VERSION return ATTACHMENT_DOWNLOADER_VERSION - except ModuleNotFoundError as e: + except ModuleNotFoundError: if retry: Version.generate() return Version.get(False) return 'unknown' - except ImportError as e: + except ImportError: if retry: Version.generate() return Version.get(False) @@ -33,5 +33,5 @@ def get(retry=True): @staticmethod def get_env_info(): - os = f"Release: {platform.release()}, Platform: {platform.platform()}" - return f"(Python: {platform.python_version()}), OS: ({os})" + os_info = f"Release: {platform.release()}, Platform: {platform.platform()}" + return f"(Python: {platform.python_version()}), OS: ({os_info})" diff --git a/requirements.txt b/requirements.txt index 8382d35..eda6f1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,6 @@ imbox==0.9.8 jinja2==2.11.3 -python-dateutil==2.8.1 \ No newline at end of file +python-dateutil==2.8.2 +pylint==2.12.2 +pytest==7.0.1 +pytest-cov==3.0.0 \ No newline at end of file diff --git a/run-pylint.sh b/run-pylint.sh new file mode 100755 index 0000000..90ee247 --- /dev/null +++ b/run-pylint.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +./venv/bin/pylint attachment_downloader diff --git a/run-tests.sh b/run-tests.sh new file mode 100755 index 0000000..2b96ef4 --- /dev/null +++ b/run-tests.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +./venv/bin/pytest --cov-report html --cov=attachment_downloader tests diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..dc6c37f --- /dev/null +++ b/setup.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -e +python3 -m pip install --user virtualenv +python3 -m venv venv +./venv/bin/pip install --upgrade pip +./venv/bin/pip install -r requirements.txt diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 From 155a88984409899365c42bf3727b3a4ed617d152 Mon Sep 17 00:00:00 2001 From: James Ridgway Date: Sat, 5 Mar 2022 14:53:18 +0000 Subject: [PATCH 2/4] Linting fixes --- attachment_downloader/cli.py | 7 +++---- attachment_downloader/version_info.py | 15 +++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/attachment_downloader/cli.py b/attachment_downloader/cli.py index 973b236..8caac2f 100644 --- a/attachment_downloader/cli.py +++ b/attachment_downloader/cli.py @@ -1,17 +1,16 @@ import datetime import sys from getpass import getpass -from optparse import OptionValueError -def valid_date(option, opt, value): +def valid_date(_, opt, value): try: parsed_value = datetime.datetime.fromisoformat(value) if not parsed_value.tzinfo: parsed_value = parsed_value.replace(tzinfo=datetime.timezone.utc) return parsed_value - except ValueError: - raise OptionValueError(f'option {opt}: invalid date format: {value}') + except ValueError as ex: + raise ValueError(f'option {opt}: invalid date format: {value}') from ex def get_password(): diff --git a/attachment_downloader/version_info.py b/attachment_downloader/version_info.py index 20ff3b8..c8abb90 100644 --- a/attachment_downloader/version_info.py +++ b/attachment_downloader/version_info.py @@ -8,16 +8,19 @@ class Version: @staticmethod def generate(): - process = subprocess.Popen(["git", "describe", "--always", "--tags"], stdout=subprocess.PIPE, stderr=None) - last_tag = process.communicate()[0].decode('ascii').strip() - version = last_tag.split('-g', maxsplit=1)[0].replace('-', '.') if '-g' in last_tag else last_tag - with open(VERSION_FILENAME, 'w') as file: - file.write(f'ATTACHMENT_DOWNLOADER_VERSION = "{version}"\n') - return version + with subprocess.Popen(["git", "describe", "--always", "--tags"], + stdout=subprocess.PIPE, + stderr=None) as process: + last_tag = process.communicate()[0].decode('ascii').strip() + version = last_tag.split('-g', maxsplit=1)[0].replace('-', '.') if '-g' in last_tag else last_tag + with open(VERSION_FILENAME, 'w',encoding='utf-8') as file: + file.write(f'ATTACHMENT_DOWNLOADER_VERSION = "{version}"\n') + return version @staticmethod def get(retry=True): try: + # pylint: disable=C0415 from attachment_downloader.version import ATTACHMENT_DOWNLOADER_VERSION return ATTACHMENT_DOWNLOADER_VERSION except ModuleNotFoundError: From 0120544ad0948c58ac961c7caf14425e346c1551 Mon Sep 17 00:00:00 2001 From: James Ridgway Date: Sat, 5 Mar 2022 15:02:41 +0000 Subject: [PATCH 3/4] Initial tests --- .coverage | Bin 53248 -> 0 bytes .gitignore | 1 + requirements.txt | 4 +++- tests/attachment_downloader/__init__.py | 0 tests/attachment_downloader/test_cli.py | 20 ++++++++++++++++++++ 5 files changed, 24 insertions(+), 1 deletion(-) delete mode 100644 .coverage create mode 100644 tests/attachment_downloader/__init__.py create mode 100644 tests/attachment_downloader/test_cli.py diff --git a/.coverage b/.coverage deleted file mode 100644 index 138200889572f31cbc9875c9d3d0464b8b9fac57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53248 zcmeI*%WoS+90%}SuN}vEZBZ3jC8{z~Rk=~)IFA-Y5h?+4s9LG72o)jNuI+KW!Mp43 zj`KJmudnLR@;`_uEI-PUEU-DpifXt9ZSSnc11oyu4XA zy?gtHqoip0fom(Vrk&DsU3*^$P18#BnxA1Z1|MVJr$QW@ZFm7MaS`?VtS#Y9N!aiPd0i=HjgaeBBP}X;SH9C(z?$Dx*x|( zt~H>OMLr^C5UqwCo;;}hMUll^jEdp;i-HS5E#Um*SoxjPx<;*1?u8FJ^uf!r zd&-aYEvkHLg;rBvzcF4c&(G_RA4Cm>d91pl!h?;{P@_3GJfi9F`O)o$d&zJk;a=3~ z2)`9|8ICRD%AwXg*k@`fh8=K66Mhnqa{5YJ8)ugCAnF~zKHS!#786S9AT$=F!CF19 z!CfWfGBo@h8Q5ELDLESt3$MB%QPWu|jg>d%M>U;;CSa``Y)D97o-Epvj)jDMm9>OeIz+U#iGcu>wZr~?U#3>=E-wU zmftvmY(KT9PzzCT>fUsr@JhNPDs4OD!6&SUy6<72r&`zH0Zv|G`J>)Kv3nWlL5jZ>u|pZg?VCmuT}qNq?`45?=xN;6F~ zIm-i8n*LIkC^%KuZpOWw>tZ0q-L`xxxp)%#$W~H0t`r<3%1qRrcI{9!0!b%n3SFoe z&#HZ{Bx{A&e3v}VA7uLTda-=*qTcT%eJ~zgtdQn%*G{vY?B<5r#-Wp0Gr39QEPcos zY#wYkz`?um6?jaGYp@wegRh%UH2T2;0SG_<0uX=z1Rwwb2tWV=5P-nR z6EO6gUgZ0K!|ZD2_vSbBh6Mr;fB*y_009U<00Izz00bZaftMigreVyk@plYXmseM- z%WKu;%VOoyl}pQ4E?+it##}jhLjTs%+S016=g;=vPQP36X=SOlBfXtk-SKMOeI-Lx zSrnCOmA)!a4P@63qW!jVI|s+#vz4+NZC85AYWllg$ER;b1bmvKhn3GJs7!|Whi3k3 z{zY$CAOHafKmY;|fB*y_009U<00Izr{RGC1S)G5|U`!fwdh)A<+@x`~l>SnIpZ^zT z9L@a6yk{0?exXe)5P$##AOHafKmY;|fB*y_0D%`5xLL?+{d2Xp@531R9e8qG>-bw+^apY1U;1CqQ-X8HPO#B&D6yNyOUYewhfN9I%29&n zkDWWqay&;_7NxkUQ;OANr7;YPkccl-q7`~UpQ$z68;Z`{uJ&mTW`1NZ;A z&3wOFIyT^;`~T<<{GUSmga8B}009U<00Izz00bZa0SG|gBn$BKfBH}Vus{F;5P$## zAOHafKmY;|fB*y_aDoN!{Qm@3E{X;L2tWV=5P$##AOHafKmY;|NCfcwA58%Q5P$## zAOHafKmY;|fB*y_aPkH4{Qu Date: Sat, 5 Mar 2022 15:05:07 +0000 Subject: [PATCH 4/4] Dropping python 3.6 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6718a3..256776d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8] + python-version: [3.7, 3.8] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }}