diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b60ba0..9bf0b97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,22 +11,22 @@ jobs: strategy: matrix: - python-version: [ 3.5, 3.6, 3.7, 3.8, 3.9 ] # pypy-3.6, pypy-3.7 are supported but a bit slow. + python-version: [ "3.6", "3.7", "3.8", "3.9", "3.10" ] # pypy-3.6, pypy-3.7 are supported but a bit slow. regex: [ "0", "1" ] exclude: # regex module doesn't play well with pypy and unicode. - - python-version: pypy-3.6 + - python-version: "pypy-3.6" regex: "1" - - python-version: pypy-3.7 + - python-version: "pypy-3.7" regex: "1" - # test regex module only with Python 3.8. - - python-version: 3.5 + # test regex module only with Python 3.9. + - python-version: "3.6" regex: "1" - - python-version: 3.6 + - python-version: "3.7" regex: "1" - - python-version: 3.7 + - python-version: "3.8" regex: "1" - - python-version: 3.9 + - python-version: "3.10" regex: "1" steps: @@ -35,11 +35,6 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Upgrade setuptools - run: | - pip install --upgrade setuptools - if: matrix.python-version == '3.5' - - name: Checkout uses: actions/checkout@v2 @@ -82,10 +77,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Setup python 3.8 + - name: Setup python 3.9 uses: actions/setup-python@v2 with: - python-version: 3.8 + python-version: 3.9 - name: Checkout uses: actions/checkout@v2 @@ -119,10 +114,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Setup python 3.8 + - name: Setup python 3.9 uses: actions/setup-python@v2 with: - python-version: 3.8 + python-version: 3.9 - name: Checkout uses: actions/checkout@v2 @@ -160,10 +155,10 @@ jobs: runs-on: windows-latest steps: - - name: Setup python 3.8 + - name: Setup python 3.9 uses: actions/setup-python@v2 with: - python-version: 3.8 + python-version: 3.9 - name: Checkout uses: actions/checkout@v2 @@ -200,10 +195,10 @@ jobs: runs-on: macos-latest steps: - - name: Setup python 3.8 + - name: Setup python 3.9 uses: actions/setup-python@v2 with: - python-version: 3.8 + python-version: 3.9 - name: Checkout uses: actions/checkout@v2 @@ -240,10 +235,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Setup python 3.8 + - name: Setup python 3.9 uses: actions/setup-python@v2 with: - python-version: 3.8 + python-version: 3.9 - name: Checkout uses: actions/checkout@v2 diff --git a/.github/workflows/mkdocs.yml b/.github/workflows/mkdocs.yml index fa9d167..d4cdcec 100644 --- a/.github/workflows/mkdocs.yml +++ b/.github/workflows/mkdocs.yml @@ -16,10 +16,10 @@ jobs: - run: mkdocs build - name: Deploy 🚀 - uses: JamesIves/github-pages-deploy-action@3.7.1 + uses: JamesIves/github-pages-deploy-action@4.1.5 with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: gh-pages - FOLDER: site - CLEAN: true - SINGLE_COMMIT: true + token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages + folder: site + clean: true + single-commit: true diff --git a/guessit/__main__.py b/guessit/__main__.py index cad2eac..36d55f1 100644 --- a/guessit/__main__.py +++ b/guessit/__main__.py @@ -85,10 +85,10 @@ def display_properties(options): properties_list = list(sorted(properties.keys())) for property_name in properties_list: property_values = properties.get(property_name) - print(2 * ' ' + '[+] %s' % (property_name,)) + print(2 * ' ' + f'[+] {property_name}') if property_values and options.get('values'): for property_value in property_values: - print(4 * ' ' + '[!] %s' % (property_value,)) + print(4 * ' ' + f'[!] {property_value}') def main(args=None): # pylint:disable=too-many-branches diff --git a/guessit/api.py b/guessit/api.py index fdf168e..9b5e7f6 100644 --- a/guessit/api.py +++ b/guessit/api.py @@ -25,16 +25,15 @@ class GuessitException(Exception): def __init__(self, string, options): super().__init__("An internal error has occured in guessit.\n" "===================== Guessit Exception Report =====================\n" - "version=%s\n" - "string=%s\n" - "options=%s\n" + f"version={__version__}\n" + f"string={str(string)}\n" + f"options={str(options)}\n" "--------------------------------------------------------------------\n" - "%s" + f"{traceback.format_exc()}" "--------------------------------------------------------------------\n" "Please report at " "https://github.com/guessit-io/guessit/issues.\n" - "====================================================================" % - (__version__, str(string), str(options), traceback.format_exc())) + "====================================================================") self.string = string self.options = options diff --git a/guessit/options.py b/guessit/options.py index efad20b..c2f96e6 100644 --- a/guessit/options.py +++ b/guessit/options.py @@ -7,8 +7,8 @@ import copy import json import os import shlex - from argparse import ArgumentParser + from importlib_resources import read_text @@ -246,17 +246,16 @@ def load_config_file(filepath): :rtype: """ if filepath.endswith('.json'): - with open(filepath) as config_file_data: + with open(filepath, encoding='utf-8') as config_file_data: return json.load(config_file_data) if filepath.endswith('.yaml') or filepath.endswith('.yml'): try: import yaml # pylint:disable=import-outside-toplevel - with open(filepath) as config_file_data: + with open(filepath, encoding='utf-8') as config_file_data: return yaml.load(config_file_data, yaml.SafeLoader) except ImportError as err: # pragma: no cover raise ConfigurationException('Configuration file extension is not supported. ' - 'PyYAML should be installed to support "%s" file' % ( - filepath,)) from err + f'PyYAML should be installed to support "{filepath}" file') from err try: # Try to load input as JSON @@ -264,7 +263,7 @@ def load_config_file(filepath): except: # pylint: disable=bare-except pass - raise ConfigurationException('Configuration file extension is not supported for "%s" file.' % (filepath,)) + raise ConfigurationException(f'Configuration file extension is not supported for "{filepath}" file.') def get_options_file_locations(homedir, cwd, yaml_supported=False): diff --git a/guessit/reutils.py b/guessit/reutils.py index 0b654d2..e6fc29e 100644 --- a/guessit/reutils.py +++ b/guessit/reutils.py @@ -25,11 +25,11 @@ def build_or_pattern(patterns, name=None, escape=False): if not or_pattern: or_pattern.append('(?') if name: - or_pattern.append('P<' + name + '>') + or_pattern.append(f'P<{name}>') else: or_pattern.append(':') else: or_pattern.append('|') - or_pattern.append('(?:%s)' % re.escape(pattern) if escape else pattern) + or_pattern.append(f'(?:{re.escape(pattern)})' if escape else pattern) or_pattern.append(')') return ''.join(or_pattern) diff --git a/guessit/rules/common/date.py b/guessit/rules/common/date.py index e513af9..1e11456 100644 --- a/guessit/rules/common/date.py +++ b/guessit/rules/common/date.py @@ -11,13 +11,21 @@ _dsep = r'[-/ \.]' _dsep_bis = r'[-/ \.x]' date_regexps = [ + # pylint:disable=consider-using-f-string re.compile(r'%s((\d{8}))%s' % (_dsep, _dsep), re.IGNORECASE), + # pylint:disable=consider-using-f-string re.compile(r'%s((\d{6}))%s' % (_dsep, _dsep), re.IGNORECASE), + # pylint:disable=consider-using-f-string re.compile(r'(?:^|[^\d])((\d{2})%s(\d{1,2})%s(\d{1,2}))(?:$|[^\d])' % (_dsep, _dsep), re.IGNORECASE), + # pylint:disable=consider-using-f-string re.compile(r'(?:^|[^\d])((\d{1,2})%s(\d{1,2})%s(\d{2}))(?:$|[^\d])' % (_dsep, _dsep), re.IGNORECASE), + # pylint:disable=consider-using-f-string re.compile(r'(?:^|[^\d])((\d{4})%s(\d{1,2})%s(\d{1,2}))(?:$|[^\d])' % (_dsep_bis, _dsep), re.IGNORECASE), + # pylint:disable=consider-using-f-string re.compile(r'(?:^|[^\d])((\d{1,2})%s(\d{1,2})%s(\d{4}))(?:$|[^\d])' % (_dsep, _dsep_bis), re.IGNORECASE), + # pylint:disable=consider-using-f-string re.compile(r'(?:^|[^\d])((\d{1,2}(?:st|nd|rd|th)?%s(?:[a-z]{3,10})%s\d{4}))(?:$|[^\d])' % (_dsep, _dsep), + # pylint:disable=consider-using-f-string re.IGNORECASE)] diff --git a/guessit/rules/common/numeral.py b/guessit/rules/common/numeral.py index 7c064fd..fa69832 100644 --- a/guessit/rules/common/numeral.py +++ b/guessit/rules/common/numeral.py @@ -81,7 +81,7 @@ def __parse_roman(value): :rtype: """ if not __romanNumeralPattern.search(value): - raise ValueError('Invalid Roman numeral: %s' % value) + raise ValueError(f'Invalid Roman numeral: {value}') result = 0 index = 0 diff --git a/guessit/rules/common/quantity.py b/guessit/rules/common/quantity.py index 8d3f21d..2a4fcdc 100644 --- a/guessit/rules/common/quantity.py +++ b/guessit/rules/common/quantity.py @@ -59,10 +59,10 @@ class Quantity(object): return not self == other def __repr__(self): - return '<{0} [{1}]>'.format(self.__class__.__name__, self) + return f'<{self.__class__.__name__} [{self}]>' def __str__(self): - return '{0}{1}'.format(self.magnitude, self.units) + return f'{self.magnitude}{self.units}' class Size(Quantity): diff --git a/guessit/rules/properties/country.py b/guessit/rules/properties/country.py index 172c299..69f8890 100644 --- a/guessit/rules/properties/country.py +++ b/guessit/rules/properties/country.py @@ -65,7 +65,7 @@ class GuessitCountryConverter(babelfish.CountryReverseConverter): # pylint: dis return 'UK' return str(babelfish.Country(alpha2)) - def reverse(self, name): # pylint:disable=arguments-differ + def reverse(self, name): # pylint:disable=arguments-renamed # exceptions come first, as they need to override a potential match # with any of the other guessers try: diff --git a/guessit/rules/properties/language.py b/guessit/rules/properties/language.py index 4ef9230..8a83d88 100644 --- a/guessit/rules/properties/language.py +++ b/guessit/rules/properties/language.py @@ -103,7 +103,7 @@ class GuessitConverter(babelfish.LanguageReverseConverter): # pylint: disable=m def convert(self, alpha3, country=None, script=None): return str(babelfish.Language(alpha3, country, script)) - def reverse(self, name): # pylint:disable=arguments-differ + def reverse(self, name): # pylint:disable=arguments-renamed name = name.lower() # exceptions come first, as they need to override a potential match # with any of the other guessers @@ -166,7 +166,7 @@ class LanguageWord(object): return LanguageWord(self.start, self.next_word.end, value, self.input_string, self.next_word.next_word) def __repr__(self): - return '<({start},{end}): {value}'.format(start=self.start, end=self.end, value=self.value) + return f'<({self.start},{self.end}): {self.value}' def to_rebulk_match(language_match): diff --git a/guessit/rules/properties/screen_size.py b/guessit/rules/properties/screen_size.py index 741e7cc..8a15398 100644 --- a/guessit/rules/properties/screen_size.py +++ b/guessit/rules/properties/screen_size.py @@ -89,7 +89,7 @@ class PostProcessScreenSize(Rule): scan_type = (values.get('scan_type') or 'p').lower() height = values['height'] if 'width' not in values: - match.value = '{0}{1}'.format(height, scan_type) + match.value = f'{height}{scan_type}' continue width = values['width'] @@ -102,9 +102,9 @@ class PostProcessScreenSize(Rule): to_append.append(aspect_ratio) if height in self.standard_heights and self.min_ar < calculated_ar < self.max_ar: - match.value = '{0}{1}'.format(height, scan_type) + match.value = f'{height}{scan_type}' else: - match.value = '{0}x{1}'.format(width, height) + match.value = f'{width}x{height}' return to_append diff --git a/guessit/rules/properties/source.py b/guessit/rules/properties/source.py index cf034e5..3686c1f 100644 --- a/guessit/rules/properties/source.py +++ b/guessit/rules/properties/source.py @@ -35,11 +35,11 @@ def source(config): # pylint:disable=unused-argument def build_source_pattern(*patterns, prefix='', suffix=''): """Helper pattern to build source pattern.""" - return [prefix + '({0})'.format(pattern) + suffix for pattern in patterns] + return [prefix + f'({pattern})' + suffix for pattern in patterns] def demote_other(match, other): # pylint: disable=unused-argument """Default conflict solver with 'other' property.""" - return other if other.name == 'other' or other.name == 'release_group' else '__default__' + return other if other.name in ['other', 'release_group'] else '__default__' rebulk.regex(*build_source_pattern('VHS', suffix=optional(rip_suffix)), value={'source': 'VHS', 'other': 'Rip'}) diff --git a/guessit/rules/properties/title.py b/guessit/rules/properties/title.py index 2a065cb..1d57de1 100644 --- a/guessit/rules/properties/title.py +++ b/guessit/rules/properties/title.py @@ -205,7 +205,7 @@ class TitleBaseRule(Rule): for ignored_match in ignored_matches: if ignored_match not in to_keep: starting = matches.chain_after(hole.start, seps, - predicate=lambda m: m == ignored_match) + predicate=lambda m, im=ignored_match: m == im) if starting: should_keep = self.should_keep(ignored_match, to_keep, matches, filepart, hole, True) if should_keep: diff --git a/guessit/test/test_api.py b/guessit/test/test_api.py index c705831..790de20 100644 --- a/guessit/test/test_api.py +++ b/guessit/test/test_api.py @@ -64,7 +64,7 @@ def test_exception(): def test_suggested_expected(): - with open(os.path.join(__location__, 'suggested.json'), 'r') as f: + with open(os.path.join(__location__, 'suggested.json'), 'r', encoding='utf-8') as f: content = json.load(f) actual = suggested_expected(content['titles']) assert actual == content['suggested'] diff --git a/guessit/test/test_main.py b/guessit/test/test_main.py index 30de4cc..a34cd4a 100644 --- a/guessit/test/test_main.py +++ b/guessit/test/test_main.py @@ -16,7 +16,7 @@ __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file # Prevent output from spamming the console @pytest.fixture(scope="function", autouse=True) def no_stdout(monkeypatch): - with open(os.devnull, "w") as f: + with open(os.devnull, "w") as f: # pylint:disable=unspecified-encoding monkeypatch.setattr(sys, "stdout", f) yield diff --git a/guessit/test/test_yml.py b/guessit/test/test_yml.py index d7cb4b5..53d7f67 100644 --- a/guessit/test/test_yml.py +++ b/guessit/test/test_yml.py @@ -3,8 +3,6 @@ # pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name import logging import os -# io.open supports encoding= in python 2.7 -from io import open # pylint: disable=redefined-builtin import babelfish import yaml # pylint:disable=wrong-import-order @@ -52,16 +50,16 @@ class EntryResult(object): if self.ok: return self.string + ': OK!' if self.warning: - return '%s%s: WARNING! (valid=%i, extra=%s)' % ('-' if self.negates else '', self.string, len(self.valid), - self.extra) + return f'{"-" if self.negates else ""}{self.string}: ' \ + f'WARNING! (valid={len(self.valid)}, extra={self.extra})' if self.error: - return '%s%s: ERROR! (valid=%i, extra=%s, missing=%s, different=%s, others=%s)' % \ - ('-' if self.negates else '', self.string, len(self.valid), self.extra, self.missing, - self.different, self.others) + return f'{"-" if self.negates else ""}{self.string}: ' \ + f'ERROR! (valid={len(self.valid)}, extra={self.extra}, ' \ + f'missing={self.missing}, different={self.different}, others={self.others})' - return '%s%s: UNKOWN! (valid=%i, extra=%s, missing=%s, different=%s, others=%s)' % \ - ('-' if self.negates else '', self.string, len(self.valid), self.extra, self.missing, self.different, - self.others) + return f'{"-" if self.negates else ""}{self.string}: ' \ + f'UNKOWN! (valid={len(self.valid)}, extra={self.extra}, ' \ + f'missing={self.missing}, different={self.different}, others={self.others})' @property def details(self): diff --git a/guessit/yamlutils.py b/guessit/yamlutils.py index f038a99..983abda 100644 --- a/guessit/yamlutils.py +++ b/guessit/yamlutils.py @@ -5,8 +5,8 @@ Options """ from collections import OrderedDict -import babelfish +import babelfish import yaml # pylint:disable=wrong-import-order from .rules.common.quantity import BitRate, FrameRate, Size @@ -35,7 +35,7 @@ class OrderedDictYAMLLoader(yaml.SafeLoader): self.flatten_mapping(node) else: # pragma: no cover raise yaml.constructor.ConstructorError(None, None, - 'expected a mapping node, but found %s' % node.id, node.start_mark) + f'expected a mapping node, but found {node.id}', node.start_mark) mapping = OrderedDict() for key_node, value_node in node.value: @@ -44,8 +44,8 @@ class OrderedDictYAMLLoader(yaml.SafeLoader): hash(key) except TypeError as exc: # pragma: no cover raise yaml.constructor.ConstructorError('while constructing a mapping', - node.start_mark, 'found unacceptable key (%s)' - % exc, key_node.start_mark) + node.start_mark, f'found unacceptable key ({exc})' + , key_node.start_mark) value = self.construct_object(value_node, deep=deep) mapping[key] = value return mapping diff --git a/pylintrc b/pylintrc index 001fca2..cd5d0f9 100644 --- a/pylintrc +++ b/pylintrc @@ -68,7 +68,7 @@ disable=unichr-builtin,backtick,delslice-method,indexing-exception,execfile-buil unpacking-in-except,import-star-module-level,buffer-builtin,round-builtin,file-builtin,reload-builtin,old-division, apply-builtin,oct-method,nonzero-method,basestring-builtin,raising-string,too-few-public-methods,too-many-arguments, too-many-instance-attributes,bad-builtin,too-many-ancestors,too-few-format-args,fixme,duplicate-code, - deprecated-lambda,too-many-nested-blocks,useless-object-inheritance,import-outside-toplevel + deprecated-lambda,too-many-nested-blocks,useless-object-inheritance,import-outside-toplevel, I diff --git a/setup.py b/setup.py index 155115d..b5a910c 100644 --- a/setup.py +++ b/setup.py @@ -45,18 +45,18 @@ args = dict(name='guessit', 'Operating System :: OS Independent', 'Intended Audience :: Developers', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Topic :: Multimedia', 'Topic :: Software Development :: Libraries :: Python Modules' ], keywords='python library release parser name filename movies series episodes animes', author='Rémi Alvergnat', author_email='toilal.dev@gmail.com', - url='http://guessit.io', + url='https://guessit.io', download_url='https://pypi.python.org/packages/source/g/guessit/guessit-%s.tar.gz' % version, license='LGPLv3', packages=find_packages(), diff --git a/tox.ini b/tox.ini index 4331201..d6229ce 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,5 @@ [tox] -envlist = py35,py36,py37,py38,py39,pypy3 - -[testenv:py39] -commands = - {envbindir}/pip install -e .[dev,test] - {envpython} setup.py test +envlist = py36,py37,py38,py39,py310,pypy3 [testenv] commands =