diff --git a/ext/feedparser/__init__.py b/ext/feedparser/__init__.py index 1e8877c080..a9607801c1 100644 --- a/ext/feedparser/__init__.py +++ b/ext/feedparser/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # @@ -32,7 +32,7 @@ __author__ = 'Kurt McKee ' __license__ = 'BSD 2-clause' -__version__ = '6.0.10' +__version__ = '6.0.11' # HTTP "User-Agent" header to send to servers when downloading feeds. # If you are embedding feedparser in a larger application, you should diff --git a/ext/feedparser/api.py b/ext/feedparser/api.py index aed3f47924..1fe40979cf 100644 --- a/ext/feedparser/api.py +++ b/ext/feedparser/api.py @@ -1,5 +1,5 @@ # The public API for feedparser -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/datetimes/__init__.py b/ext/feedparser/datetimes/__init__.py index 21487a0f5b..2837838ba6 100644 --- a/ext/feedparser/datetimes/__init__.py +++ b/ext/feedparser/datetimes/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/datetimes/asctime.py b/ext/feedparser/datetimes/asctime.py index c4b162497a..99416f664a 100644 --- a/ext/feedparser/datetimes/asctime.py +++ b/ext/feedparser/datetimes/asctime.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/datetimes/greek.py b/ext/feedparser/datetimes/greek.py index 7f433fed80..3a4db71485 100644 --- a/ext/feedparser/datetimes/greek.py +++ b/ext/feedparser/datetimes/greek.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/datetimes/hungarian.py b/ext/feedparser/datetimes/hungarian.py index 691a6ebc24..63fa9afd1b 100644 --- a/ext/feedparser/datetimes/hungarian.py +++ b/ext/feedparser/datetimes/hungarian.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/datetimes/iso8601.py b/ext/feedparser/datetimes/iso8601.py index b7463a52a3..e78764592f 100644 --- a/ext/feedparser/datetimes/iso8601.py +++ b/ext/feedparser/datetimes/iso8601.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/datetimes/korean.py b/ext/feedparser/datetimes/korean.py index 788d466681..5fe4eb6f9d 100644 --- a/ext/feedparser/datetimes/korean.py +++ b/ext/feedparser/datetimes/korean.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/datetimes/perforce.py b/ext/feedparser/datetimes/perforce.py index d52a838b3b..e53e5ff67b 100644 --- a/ext/feedparser/datetimes/perforce.py +++ b/ext/feedparser/datetimes/perforce.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/datetimes/rfc822.py b/ext/feedparser/datetimes/rfc822.py index 871e18fddb..12e699c84f 100644 --- a/ext/feedparser/datetimes/rfc822.py +++ b/ext/feedparser/datetimes/rfc822.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/datetimes/w3dtf.py b/ext/feedparser/datetimes/w3dtf.py index 6fb2c54593..9b71af1fa3 100644 --- a/ext/feedparser/datetimes/w3dtf.py +++ b/ext/feedparser/datetimes/w3dtf.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/encodings.py b/ext/feedparser/encodings.py index f939f468b4..5ae74af689 100644 --- a/ext/feedparser/encodings.py +++ b/ext/feedparser/encodings.py @@ -1,5 +1,5 @@ # Character encoding routines -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # @@ -26,9 +26,9 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -import cgi import codecs import re +import typing as t try: try: @@ -68,6 +68,30 @@ def lazy_chardet_encoding(data): RE_XML_PI_ENCODING = re.compile(br'^<\?.*encoding=[\'"](.*?)[\'"].*\?>') +def parse_content_type(line: str) -> t.Tuple[str, str]: + """Parse an HTTP Content-Type header. + + The return value will be a tuple of strings: + the MIME type, and the value of the "charset" (if any). + + This is a custom replacement for Python's cgi.parse_header(). + The cgi module will be removed in Python 3.13. + """ + + chunks = line.split(";") + if not chunks: + return "", "" + + mime_type = chunks[0].strip() + charset_value = "" + for chunk in chunks[1:]: + key, _, value = chunk.partition("=") + if key.strip().lower() == "charset": + charset_value = value.strip().strip("\"'") + + return mime_type, charset_value + + def convert_to_utf8(http_headers, data, result): """Detect and convert the character encoding to UTF-8. @@ -181,10 +205,7 @@ def convert_to_utf8(http_headers, data, result): # XML declaration encoding, and HTTP encoding, following the # heuristic defined in RFC 3023. http_content_type = http_headers.get('content-type') or '' - http_content_type, params = cgi.parse_header(http_content_type) - http_encoding = params.get('charset', '').replace("'", "") - if isinstance(http_encoding, bytes): - http_encoding = http_encoding.decode('utf-8', 'ignore') + http_content_type, http_encoding = parse_content_type(http_content_type) acceptable_content_type = 0 application_content_types = ('application/xml', 'application/xml-dtd', diff --git a/ext/feedparser/exceptions.py b/ext/feedparser/exceptions.py index 3a022811e0..0da8152ff3 100644 --- a/ext/feedparser/exceptions.py +++ b/ext/feedparser/exceptions.py @@ -1,5 +1,5 @@ # Exceptions used throughout feedparser -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/html.py b/ext/feedparser/html.py index 3ffa9b8883..0aae18f6a8 100644 --- a/ext/feedparser/html.py +++ b/ext/feedparser/html.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/http.py b/ext/feedparser/http.py index 9d441038d2..1516eabfde 100644 --- a/ext/feedparser/http.py +++ b/ext/feedparser/http.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/mixin.py b/ext/feedparser/mixin.py index 745423694b..c4265e750f 100644 --- a/ext/feedparser/mixin.py +++ b/ext/feedparser/mixin.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/namespaces/_base.py b/ext/feedparser/namespaces/_base.py index 6478a76c88..cb2b2a98ca 100644 --- a/ext/feedparser/namespaces/_base.py +++ b/ext/feedparser/namespaces/_base.py @@ -1,5 +1,5 @@ # Support for the Atom, RSS, RDF, and CDF feed formats -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/namespaces/admin.py b/ext/feedparser/namespaces/admin.py index 7421834834..88b70013d3 100644 --- a/ext/feedparser/namespaces/admin.py +++ b/ext/feedparser/namespaces/admin.py @@ -1,5 +1,5 @@ # Support for the administrative elements extension -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/namespaces/cc.py b/ext/feedparser/namespaces/cc.py index 6735c5fe87..9e482d1b4d 100644 --- a/ext/feedparser/namespaces/cc.py +++ b/ext/feedparser/namespaces/cc.py @@ -1,5 +1,5 @@ # Support for the Creative Commons licensing extensions -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/namespaces/dc.py b/ext/feedparser/namespaces/dc.py index a89221d2d7..fdf4cc11fc 100644 --- a/ext/feedparser/namespaces/dc.py +++ b/ext/feedparser/namespaces/dc.py @@ -1,5 +1,5 @@ # Support for the Dublin Core metadata extensions -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/namespaces/georss.py b/ext/feedparser/namespaces/georss.py index 786a926fbe..2f30350686 100644 --- a/ext/feedparser/namespaces/georss.py +++ b/ext/feedparser/namespaces/georss.py @@ -1,5 +1,5 @@ # Support for the GeoRSS format -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/namespaces/itunes.py b/ext/feedparser/namespaces/itunes.py index a50a0ea89a..7cebfae9a0 100644 --- a/ext/feedparser/namespaces/itunes.py +++ b/ext/feedparser/namespaces/itunes.py @@ -1,5 +1,5 @@ # Support for the iTunes format -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/namespaces/mediarss.py b/ext/feedparser/namespaces/mediarss.py index 2298ad2f70..00a07afbcb 100644 --- a/ext/feedparser/namespaces/mediarss.py +++ b/ext/feedparser/namespaces/mediarss.py @@ -1,5 +1,5 @@ # Support for the Media RSS format -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/namespaces/psc.py b/ext/feedparser/namespaces/psc.py index a440bd6837..519bee1d8e 100644 --- a/ext/feedparser/namespaces/psc.py +++ b/ext/feedparser/namespaces/psc.py @@ -1,5 +1,5 @@ # Support for the Podlove Simple Chapters format -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/parsers/loose.py b/ext/feedparser/parsers/loose.py index d6c97e1060..ee0b58b138 100644 --- a/ext/feedparser/parsers/loose.py +++ b/ext/feedparser/parsers/loose.py @@ -1,5 +1,5 @@ # The loose feed parser that interfaces with an SGML parsing library -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/parsers/strict.py b/ext/feedparser/parsers/strict.py index 9bca11292d..d40797a7aa 100644 --- a/ext/feedparser/parsers/strict.py +++ b/ext/feedparser/parsers/strict.py @@ -1,5 +1,5 @@ # The strict feed parser that interfaces with an XML parsing library -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/sanitizer.py b/ext/feedparser/sanitizer.py index 8052c5cfd0..597a72b87f 100644 --- a/ext/feedparser/sanitizer.py +++ b/ext/feedparser/sanitizer.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/sgml.py b/ext/feedparser/sgml.py index 7dc5bfdd16..ae5edf2a21 100644 --- a/ext/feedparser/sgml.py +++ b/ext/feedparser/sgml.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/urls.py b/ext/feedparser/urls.py index bc7cda56e3..1155f26e61 100644 --- a/ext/feedparser/urls.py +++ b/ext/feedparser/urls.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/feedparser/util.py b/ext/feedparser/util.py index d5af9e2187..5ad3a84a0e 100644 --- a/ext/feedparser/util.py +++ b/ext/feedparser/util.py @@ -1,4 +1,4 @@ -# Copyright 2010-2022 Kurt McKee +# Copyright 2010-2023 Kurt McKee # Copyright 2002-2008 Mark Pilgrim # All rights reserved. # diff --git a/ext/markdown2.py b/ext/markdown2.py index 368b095429..4d00098449 100644 --- a/ext/markdown2.py +++ b/ext/markdown2.py @@ -106,7 +106,7 @@ # not yet sure if there implications with this. Compare 'pydoc sre' # and 'perldoc perlre'. -__version_info__ = (2, 4, 11) +__version_info__ = (2, 4, 13) __version__ = '.'.join(map(str, __version_info__)) __author__ = "Trent Mick" @@ -783,8 +783,15 @@ def _detab(self, text): def _hash_html_block_sub(self, match, raw=False): if isinstance(match, str): html = match + tag = None else: html = match.group(1) + try: + tag = match.group(2) + except IndexError: + tag = None + + tag = tag or re.match(r'.*?<(\S).*?>', html).group(1) if raw and self.safe_mode: html = self._sanitize_html(html) @@ -793,9 +800,17 @@ def _hash_html_block_sub(self, match, raw=False): m = self._html_markdown_attr_re.search(first_line) if m: lines = html.split('\n') + # if MD is on same line as opening tag then split across two lines + lines = list(filter(None, (re.split(r'(.*?<%s.*markdown=.*?>)' % tag, lines[0])))) + lines[1:] + # if MD on same line as closing tag, split across two lines + lines = lines[:-1] + list(filter(None, re.split(r'(\s*?.*?$)' % tag, lines[-1]))) + # extract key sections of the match + first_line = lines[0] middle = '\n'.join(lines[1:-1]) last_line = lines[-1] + # remove `markdown="1"` attr from tag first_line = first_line[:m.start()] + first_line[m.end():] + # hash the HTML segments to protect them f_key = _hash_text(first_line) self.html_blocks[f_key] = first_line l_key = _hash_text(last_line) @@ -1238,24 +1253,24 @@ def _do_tables(self, text): """ less_than_tab = self.tab_width - 1 table_re = re.compile(r''' - (?:(?<=\n\n)|\A\n?) # leading blank line + (?:(?<=\n)|\A\n?) # leading blank line ^[ ]{0,%d} # allowed whitespace - (.*[|].*) \n # $1: header row (at least one pipe) + (.*[|].*)[ ]*\n # $1: header row (at least one pipe) ^[ ]{0,%d} # allowed whitespace ( # $2: underline row # underline row with leading bar - (?: \|\ *:?-+:?\ * )+ \|? \s? \n + (?: \|\ *:?-+:?\ * )+ \|? \s?[ ]*\n | # or, underline row without leading bar - (?: \ *:?-+:?\ *\| )+ (?: \ *:?-+:?\ * )? \s? \n + (?: \ *:?-+:?\ *\| )+ (?: \ *:?-+:?\ * )? \s?[ ]*\n ) ( # $3: data rows (?: ^[ ]{0,%d}(?!\ ) # ensure line begins with 0 to less_than_tab spaces - .*\|.* \n + .*\|.*[ ]*\n )+ ) ''' % (less_than_tab, less_than_tab, less_than_tab), re.M | re.X) @@ -1351,15 +1366,20 @@ def _run_span_gamut(self, text): text = self._do_smart_punctuation(text) # Do hard breaks: - if 'breaks' in self.extras: - break_tag = ")", break_tag, text) + on_backslash = self.extras.get('breaks', {}).get('on_backslash', False) + on_newline = self.extras.get('breaks', {}).get('on_newline', False) + + if on_backslash and on_newline: + pattern = r' *\\?' + elif on_backslash: + pattern = r'(?: *\\| {2,})' + elif on_newline: + pattern = r' *' else: - text = re.sub(r" {2,}\n", " )", break_tag, text) return text @@ -1775,7 +1795,8 @@ def _do_links(self, text): curr_pos = start_idx + 1 else: # This id isn't defined, leave the markup alone. - curr_pos = match.end() + # set current pos to end of link title and continue from there + curr_pos = p continue # Otherwise, it isn't markup. @@ -2395,9 +2416,9 @@ def _do_tg_spoiler(self, text): text = self._tg_spoiler_re.sub(r"\1", text) return text - _strong_re = re.compile(r"(\*\*|__)(?=\S)(.*\S)\1", re.S) + _strong_re = re.compile(r"(\*\*|__)(?=\S)(.+?[*_]?)(?<=\S)\1", re.S) _em_re = r"(\*|_)(?=\S)(.*?\S)\1" - _code_friendly_strong_re = re.compile(r"\*\*(?=\S)(.*\S)\*\*", re.S) + _code_friendly_strong_re = re.compile(r"\*\*(?=\S)(.+?[*_]?)(?<=\S)\*\*", re.S) _code_friendly_em_re = r"\*(?=\S)(.+?)\*" def _do_italics_and_bold(self, text): if self.extras.get('middle-word-em', True) is False: @@ -2623,7 +2644,7 @@ def _encode_amps_and_angles(self, text): text = self._naked_gt_re.sub('>', text) return text - _incomplete_tags_re = re.compile(r"<(/?\w+?(?!\w)\s*?.+?[\s/]+?)") + _incomplete_tags_re = re.compile(r"<(!--|/?\w+?(?!\w)\s*?.+?[\s/]+?)") def _encode_incomplete_tags(self, text): if self.safe_mode not in ("replace", "escape"): diff --git a/ext/readme.md b/ext/readme.md index 650601f1b8..fe7f4114cd 100644 --- a/ext/readme.md +++ b/ext/readme.md @@ -20,7 +20,7 @@ ext | **`deprecated`** | [1.2.3](https://pypi.org/project/deprecated/1.2.3/) | ` ext | **`diskcache`** | [5.2.1](https://pypi.org/project/diskcache/5.2.1/) | `imdbpie` | - ext | `dogpile.cache` | [1.2.1](https://pypi.org/project/dogpile.cache/1.2.1/) | **`medusa`**, `subliminal` | Module: `dogpile` ext | **`enzyme`** | pymedusa/[665cf69](https://github.com/pymedusa/enzyme/tree/665cf6948aab1c249dcc99bd9624a81d17b3302a) | `knowit`, `subliminal` | - -ext | **`feedparser`** | [6.0.10](https://pypi.org/project/feedparser/6.0.10/) | **`medusa`** | Requires `sgmllib3k` on Python 3 +ext | **`feedparser`** | [6.0.11](https://pypi.org/project/feedparser/6.0.11/) | **`medusa`** | Requires `sgmllib3k` on Python 3 ext | **`gntp`** | [1.0.3](https://pypi.org/project/gntp/1.0.3/) | **`medusa`** | - ext | **`guessit`** | [3.4.2](https://pypi.org/project/guessit/3.4.2/) | **`medusa`**, `subliminal` | - ext | **`html5lib`** | [1.1](https://pypi.org/project/html5lib/1.1/) | **`medusa`** (via `beautifulsoup4`) | - @@ -31,7 +31,7 @@ ext | `importlib-resources` | [5.4.0](https://pypi.org/project/importlib-resourc ext | `jsonrpclib-pelix` | [0.4.3.2](https://pypi.org/project/jsonrpclib-pelix/0.4.3.2/) | **`medusa`** | Module: `jsonrpclib` ext | **`knowit`** | [0.4.0](https://github.com/ratoaq2/knowit/tree/0.4.0) | **`medusa`** | - ext | `Mako` | [1.2.4](https://pypi.org/project/Mako/1.2.4/) | **`medusa`** | Module: `mako` -ext | `markdown2` | [2.4.11](https://pypi.org/project/markdown2/2.4.11/) | **`medusa`** | File: `markdown2.py` +ext | `markdown2` | [2.4.13](https://pypi.org/project/markdown2/2.4.13/) | **`medusa`** | File: `markdown2.py` ext | `MarkupSafe` | [1.1.1](https://pypi.org/project/MarkupSafe/1.1.1/) | `Mako` | Module: `markupsafe` ext | **`msgpack`** | [0.5.6](https://pypi.org/project/msgpack/0.5.6/) | `CacheControl` | - ext | **`oauthlib`** | [3.0.0](https://pypi.org/project/oauthlib/3.0.0/) | `requests-oauthlib` | - diff --git a/requirements.txt b/requirements.txt index 4217d7a745..413f200ec0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ contextlib2==21.6.0 deluge-client==1.9.0 dogpile.cache==1.2.1 enzyme @ https://codeload.github.com/pymedusa/enzyme/tar.gz/665cf6948aab1c249dcc99bd9624a81d17b3302a -feedparser==6.0.10 +feedparser==6.0.11 gntp==1.0.3 guessit==3.4.2 html5lib==1.1 @@ -18,7 +18,7 @@ imdbpie==5.6.5 jsonrpclib-pelix==0.4.3.2 knowit==0.4.0 Mako==1.2.4 -markdown2==2.4.11 +markdown2==2.4.13 profilehooks==1.12.0 PyGithub==1.45 PyJWT==2.7.0