no log: updating some dependencies that were having pending PR.

This commit is contained in:
morpheus65535 2023-03-22 21:36:58 -04:00
parent 3a10df7724
commit ec65f05399
23 changed files with 2796 additions and 28135 deletions

View File

@ -0,0 +1 @@
__version__ = "0.5.0"

View File

@ -1,4 +1,5 @@
import codecs import codecs
from collections.abc import MutableMapping
import logging import logging
import os import os
import pickle import pickle
@ -7,14 +8,6 @@ import tempfile
import appdirs import appdirs
try:
from collections.abc import MutableMapping
unicode = str
except ImportError:
# Python 2 imports
from collections import MutableMapping
FileNotFoundError = IOError
from .posixemulation import rename from .posixemulation import rename
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -33,14 +26,14 @@ class FileCache(MutableMapping):
.. NOTE:: .. NOTE::
Keys and values are always stored as :class:`bytes` objects. If data Keys and values are always stored as :class:`bytes` objects. If data
serialization is enabled, keys are returned as :class:`str` or serialization is enabled, keys are returned as :class:`str` objects.
:class:`unicode` objects.
If data serialization is disabled, keys are returned as a If data serialization is disabled, keys are returned as a
:class:`bytes` object. :class:`bytes` object.
:param str appname: The app/script the cache should be associated with. :param str appname: The app/script the cache should be associated with.
:param str flag: How the cache should be opened. See below for details. :param str flag: How the cache should be opened. See below for details.
:param mode: The Unix mode for the cache files or False to prevent changing permissions. :param mode: The Unix mode for the cache files or False to prevent changing
permissions.
:param str keyencoding: The encoding the keys use, defaults to 'utf-8'. :param str keyencoding: The encoding the keys use, defaults to 'utf-8'.
This is used if *serialize* is ``False``; the keys are treated as This is used if *serialize* is ``False``; the keys are treated as
:class:`bytes` objects. :class:`bytes` objects.
@ -85,57 +78,66 @@ class FileCache(MutableMapping):
""" """
def __init__(self, appname, flag='c', mode=0o666, keyencoding='utf-8', def __init__(
serialize=True, app_cache_dir=None): self,
appname,
flag="c",
mode=0o666,
keyencoding="utf-8",
serialize=True,
app_cache_dir=None,
):
"""Initialize a :class:`FileCache` object.""" """Initialize a :class:`FileCache` object."""
if not isinstance(flag, str): if not isinstance(flag, str):
raise TypeError("flag must be str not '{}'".format(type(flag))) raise TypeError("flag must be str not '{}'".format(type(flag)))
elif flag[0] not in 'rwcn': elif flag[0] not in "rwcn":
raise ValueError("invalid flag: '{}', first flag must be one of " raise ValueError(
"'r', 'w', 'c' or 'n'".format(flag)) "invalid flag: '{}', first flag must be one of "
elif len(flag) > 1 and flag[1] != 's': "'r', 'w', 'c' or 'n'".format(flag)
raise ValueError("invalid flag: '{}', second flag must be " )
"'s'".format(flag)) elif len(flag) > 1 and flag[1] != "s":
raise ValueError(
"invalid flag: '{}', second flag must be " "'s'".format(flag)
)
appname, subcache = self._parse_appname(appname) appname, subcache = self._parse_appname(appname)
if 'cache' in subcache: if "cache" in subcache:
raise ValueError("invalid subcache name: 'cache'.") raise ValueError("invalid subcache name: 'cache'.")
self._is_subcache = bool(subcache) self._is_subcache = bool(subcache)
if not app_cache_dir: if not app_cache_dir:
app_cache_dir = appdirs.user_cache_dir(appname, appname) app_cache_dir = appdirs.user_cache_dir(appname, appname)
subcache_dir = os.path.join(app_cache_dir, *subcache) subcache_dir = os.path.join(app_cache_dir, *subcache)
self.cache_dir = os.path.join(subcache_dir, 'cache') self.cache_dir = os.path.join(subcache_dir, "cache")
exists = os.path.exists(self.cache_dir) exists = os.path.exists(self.cache_dir)
if len(flag) > 1 and flag[1] == 's': if len(flag) > 1 and flag[1] == "s":
self._sync = True self._sync = True
else: else:
self._sync = False self._sync = False
self._buffer = {} self._buffer = {}
if exists and 'n' in flag: if exists and "n" in flag:
self.clear() self.clear()
self.create() self.create()
elif not exists and ('c' in flag or 'n' in flag): elif not exists and ("c" in flag or "n" in flag):
self.create() self.create()
elif not exists: elif not exists:
raise FileNotFoundError("no such directory: '{}'".format( raise FileNotFoundError("no such directory: '{}'".format(self.cache_dir))
self.cache_dir))
self._flag = 'rb' if 'r' in flag else 'wb' self._flag = "rb" if "r" in flag else "wb"
self._mode = mode self._mode = mode
self._keyencoding = keyencoding self._keyencoding = keyencoding
self._serialize = serialize self._serialize = serialize
def _parse_appname(self, appname): def _parse_appname(self, appname):
"""Splits an appname into the appname and subcache components.""" """Splits an appname into the appname and subcache components."""
components = appname.split('.') components = appname.split(".")
return components[0], components[1:] return components[0], components[1:]
def create(self): def create(self):
"""Create the write buffer and cache directory.""" """Create the write buffer and cache directory."""
if not self._sync and not hasattr(self, '_buffer'): if not self._sync and not hasattr(self, "_buffer"):
self._buffer = {} self._buffer = {}
if not os.path.exists(self.cache_dir): if not os.path.exists(self.cache_dir):
os.makedirs(self.cache_dir) os.makedirs(self.cache_dir)
@ -195,11 +197,11 @@ class FileCache(MutableMapping):
:class:`str`. :class:`str`.
""" """
if isinstance(key, str) or isinstance(key, unicode): if isinstance(key, str):
key = key.encode(self._keyencoding) key = key.encode(self._keyencoding)
elif not isinstance(key, bytes): elif not isinstance(key, bytes):
raise TypeError("key must be bytes or str") raise TypeError("key must be bytes or str")
return codecs.encode(key, 'hex_codec').decode(self._keyencoding) return codecs.encode(key, "hex_codec").decode(self._keyencoding)
def _decode_key(self, key): def _decode_key(self, key):
"""Decode key using hex_codec to retrieve the original key. """Decode key using hex_codec to retrieve the original key.
@ -208,7 +210,7 @@ class FileCache(MutableMapping):
Keys are returned as :class:`bytes` if serialization is disabled. Keys are returned as :class:`bytes` if serialization is disabled.
""" """
bkey = codecs.decode(key.encode(self._keyencoding), 'hex_codec') bkey = codecs.decode(key.encode(self._keyencoding), "hex_codec")
return bkey.decode(self._keyencoding) if self._serialize else bkey return bkey.decode(self._keyencoding) if self._serialize else bkey
def _dumps(self, value): def _dumps(self, value):
@ -228,8 +230,10 @@ class FileCache(MutableMapping):
def _all_filenames(self): def _all_filenames(self):
"""Return a list of absolute cache filenames""" """Return a list of absolute cache filenames"""
try: try:
return [os.path.join(self.cache_dir, filename) for filename in return [
os.listdir(self.cache_dir)] os.path.join(self.cache_dir, filename)
for filename in os.listdir(self.cache_dir)
]
except (FileNotFoundError, OSError): except (FileNotFoundError, OSError):
return [] return []
@ -252,12 +256,8 @@ class FileCache(MutableMapping):
def _read_from_file(self, filename): def _read_from_file(self, filename):
"""Read data from filename.""" """Read data from filename."""
try: with open(filename, "rb") as f:
with open(filename, 'rb') as f: return self._loads(f.read())
return self._loads(f.read())
except (IOError, OSError):
logger.warning('Error opening file: {}'.format(filename))
return None
def __setitem__(self, key, value): def __setitem__(self, key, value):
ekey = self._encode_key(key) ekey = self._encode_key(key)
@ -281,17 +281,17 @@ class FileCache(MutableMapping):
def __delitem__(self, key): def __delitem__(self, key):
ekey = self._encode_key(key) ekey = self._encode_key(key)
filename = self._key_to_filename(ekey) found_in_buffer = hasattr(self, "_buffer") and ekey in self._buffer
if not self._sync: if not self._sync:
try: try:
del self._buffer[ekey] del self._buffer[ekey]
except KeyError: except KeyError:
if filename not in self._all_filenames(): pass
raise KeyError(key) filename = self._key_to_filename(ekey)
try: if filename in self._all_filenames():
os.remove(filename) os.remove(filename)
except (IOError, OSError): elif not found_in_buffer:
pass raise KeyError(key)
def __iter__(self): def __iter__(self):
for key in self._all_keys(): for key in self._all_keys():
@ -303,3 +303,9 @@ class FileCache(MutableMapping):
def __contains__(self, key): def __contains__(self, key):
ekey = self._encode_key(key) ekey = self._encode_key(key)
return ekey in self._all_keys() return ekey in self._all_keys()
def __enter__(self):
return self
def __exit__(self, type_, value, traceback):
self.close()

View File

@ -1,17 +1,17 @@
from .utils import ( from .utils import (
Result,
get_fld, get_fld,
get_tld, get_tld,
get_tld_names, get_tld_names,
is_tld, is_tld,
parse_tld, parse_tld,
Result,
update_tld_names, update_tld_names,
) )
__title__ = "tld" __title__ = "tld"
__version__ = "0.12.6" __version__ = "0.13"
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ( __all__ = (
"get_fld", "get_fld",

View File

@ -1,16 +1,13 @@
from codecs import open as codecs_open
import logging import logging
from codecs import open as codecs_open
from typing import Dict, ItemsView, Optional, Union
from urllib.request import urlopen from urllib.request import urlopen
from typing import Optional, Dict, Union, ItemsView
from .exceptions import ( from .exceptions import TldImproperlyConfigured, TldIOError
TldIOError,
TldImproperlyConfigured,
)
from .helpers import project_dir from .helpers import project_dir
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ( __all__ = (
"BaseTLDSourceParser", "BaseTLDSourceParser",
@ -98,17 +95,15 @@ class BaseTLDSourceParser(metaclass=Registry):
try: try:
remote_file = urlopen(cls.source_url) remote_file = urlopen(cls.source_url)
local_file_abs_path = project_dir(cls.local_path) local_file_abs_path = project_dir(cls.local_path)
local_file = codecs_open( local_file = codecs_open(local_file_abs_path, "wb", encoding="utf8")
local_file_abs_path, "wb", encoding="utf8"
)
local_file.write(remote_file.read().decode("utf8")) local_file.write(remote_file.read().decode("utf8"))
local_file.close() local_file.close()
remote_file.close() remote_file.close()
LOGGER.debug( LOGGER.info(
f"Fetched '{cls.source_url}' as '{local_file_abs_path}'" f"Fetched '{cls.source_url}' as '{local_file_abs_path}'"
) )
except Exception as err: except Exception as err:
LOGGER.debug( LOGGER.error(
f"Failed fetching '{cls.source_url}'. Reason: {str(err)}" f"Failed fetching '{cls.source_url}'. Reason: {str(err)}"
) )
if fail_silently: if fail_silently:

View File

@ -1,8 +1,9 @@
from typing import Any from typing import Any
from . import defaults from . import defaults
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2020 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ( __all__ = (
"get_setting", "get_setting",

View File

@ -1,7 +1,7 @@
from os.path import dirname from os.path import dirname
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2020 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ( __all__ = (
"DEBUG", "DEBUG",

View File

@ -1,5 +1,5 @@
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ( __all__ = (
"TldBadUrl", "TldBadUrl",

View File

@ -3,7 +3,7 @@ from os.path import abspath, join
from .conf import get_setting from .conf import get_setting
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ( __all__ = (
"project_dir", "project_dir",

View File

@ -1,8 +1,9 @@
import warnings import warnings
from .base import Registry
from .base import Registry # noqa
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ("Registry",) __all__ = ("Registry",)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ from typing import Any, Dict
from urllib.parse import SplitResult from urllib.parse import SplitResult
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ("Result",) __all__ = ("Result",)

View File

@ -1,8 +0,0 @@
import unittest
from .test_core import *
from .test_commands import *
if __name__ == "__main__":
unittest.main()

View File

@ -1,11 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from functools import lru_cache
import logging import logging
import socket import socket
from functools import lru_cache
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ( __all__ = (
"internet_available_only", "internet_available_only",

View File

@ -1,14 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging import logging
import subprocess
import unittest import unittest
import subprocess from .base import internet_available_only, log_info
from .base import log_info, internet_available_only
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("TestCommands",) __all__ = ("TestCommands",)

View File

@ -2,12 +2,11 @@
import copy import copy
import logging import logging
from os.path import abspath, join
import unittest import unittest
from os.path import abspath, join
from tempfile import gettempdir from tempfile import gettempdir
from typing import Type from typing import Type
from urllib.parse import SplitResult, urlsplit
from urllib.parse import urlsplit, SplitResult
from faker import Faker # type: ignore from faker import Faker # type: ignore
@ -22,23 +21,22 @@ from ..exceptions import (
) )
from ..helpers import project_dir from ..helpers import project_dir
from ..utils import ( from ..utils import (
BaseMozillaTLDSourceParser,
MozillaTLDSourceParser,
get_fld, get_fld,
get_tld, get_tld,
get_tld_names, get_tld_names,
get_tld_names_container, get_tld_names_container,
is_tld, is_tld,
MozillaTLDSourceParser,
BaseMozillaTLDSourceParser,
parse_tld, parse_tld,
reset_tld_names, reset_tld_names,
update_tld_names, update_tld_names,
update_tld_names_cli, update_tld_names_cli,
) )
from .base import internet_available_only, log_info from .base import internet_available_only, log_info
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ("TestCore",) __all__ = ("TestCore",)
@ -647,9 +645,7 @@ class TestCore(unittest.TestCase):
Assert raise TldIOError on wrong `NAMES_SOURCE_URL` for `parse_tld`. Assert raise TldIOError on wrong `NAMES_SOURCE_URL` for `parse_tld`.
""" """
parser_class = self.get_custom_parser_class( parser_class = self.get_custom_parser_class(source_url="i-do-not-exist")
source_url="i-do-not-exist"
)
parsed_tld = parse_tld( parsed_tld = parse_tld(
self.bad_url, fail_silently=False, parser_class=parser_class self.bad_url, fail_silently=False, parser_class=parser_class
) )
@ -810,9 +806,7 @@ class TestCore(unittest.TestCase):
"""Test len of the trie nodes.""" """Test len of the trie nodes."""
get_tld("http://delusionalinsanity.com") get_tld("http://delusionalinsanity.com")
tld_names = get_tld_names_container() tld_names = get_tld_names_container()
self.assertGreater( self.assertGreater(len(tld_names[MozillaTLDSourceParser.local_path]), 0)
len(tld_names[MozillaTLDSourceParser.local_path]), 0
)
@log_info @log_info
def test_25_get_tld_names_no_arguments(self): def test_25_get_tld_names_no_arguments(self):
@ -842,3 +836,16 @@ class TestCore(unittest.TestCase):
fragment="", fragment="",
), ),
) )
@log_info
def test_27_tld_fail_silently_pass(self):
"""Test `get_tld` bad URL patterns that would raise exception
if `fail_silently` isn't `True`.
"""
res = []
bad_url = ["https://user:password[@host.com", "https://user[@host.com"]
for url in bad_url:
_res = get_tld(url, fail_silently=True)
self.assertEqual(_res, None)
res.append(_res)
return res

View File

@ -1,7 +1,7 @@
import unittest import unittest
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ("TestRegistry",) __all__ = ("TestRegistry",)
@ -11,4 +11,4 @@ class TestRegistry(unittest.TestCase):
def test_import_from_registry(self): def test_import_from_registry(self):
"""Test import from deprecated `valuta.registry` module.""" """Test import from deprecated `valuta.registry` module."""
from ..registry import Registry from ..registry import Registry # noqa

View File

@ -1,5 +1,5 @@
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ( __all__ = (
"Trie", "Trie",

View File

@ -1,13 +1,12 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import argparse import argparse
import sys
from codecs import open as codecs_open from codecs import open as codecs_open
from functools import lru_cache from functools import lru_cache
# codecs_open = open
from os.path import isabs from os.path import isabs
import sys from typing import Dict, List, Optional, Tuple, Type, Union
from typing import Dict, Type, Union, Tuple, List, Optional from urllib.parse import SplitResult, urlsplit
from urllib.parse import urlsplit, SplitResult
from .base import BaseTLDSourceParser, Registry from .base import BaseTLDSourceParser, Registry
from .exceptions import ( from .exceptions import (
@ -17,11 +16,14 @@ from .exceptions import (
TldIOError, TldIOError,
) )
from .helpers import project_dir from .helpers import project_dir
from .trie import Trie
from .result import Result from .result import Result
from .trie import Trie
# codecs_open = open
__author__ = "Artur Barseghyan" __author__ = "Artur Barseghyan"
__copyright__ = "2013-2021 Artur Barseghyan" __copyright__ = "2013-2023 Artur Barseghyan"
__license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later" __license__ = "MPL-1.1 OR GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = ( __all__ = (
"BaseMozillaTLDSourceParser", "BaseMozillaTLDSourceParser",
@ -132,9 +134,7 @@ def update_tld_names_cli() -> int:
parser_uid = args.parser_uid parser_uid = args.parser_uid
fail_silently = args.fail_silently fail_silently = args.fail_silently
return int( return int(
not update_tld_names( not update_tld_names(parser_uid=parser_uid, fail_silently=fail_silently)
parser_uid=parser_uid, fail_silently=fail_silently
)
) )
@ -229,7 +229,7 @@ class BaseMozillaTLDSourceParser(BaseTLDSourceParser):
update_tld_names_container(cls.local_path, trie) update_tld_names_container(cls.local_path, trie)
local_file.close() local_file.close()
except IOError as err: except IOError:
# Grab the file # Grab the file
cls.update_tld_names(fail_silently=fail_silently) cls.update_tld_names(fail_silently=fail_silently)
# Increment ``retry_count`` in order to avoid infinite loops # Increment ``retry_count`` in order to avoid infinite loops
@ -314,20 +314,14 @@ def process_url(
parsed_url = urlsplit(url) parsed_url = urlsplit(url)
except ValueError as e: except ValueError as e:
if fail_silently: if fail_silently:
parsed_url = url return None, None, url
else: else:
raise e raise e
else: else:
parsed_url = url parsed_url = url
# Get (sub) domain name # Get (sub) domain name
try: domain_name = parsed_url.hostname
domain_name = parsed_url.hostname
except AttributeError as e:
if fail_silently:
domain_name = None
else:
raise e
if not domain_name: if not domain_name:
if fail_silently: if fail_silently:

View File

@ -129,9 +129,9 @@ webencodings==0.5.1
# Required-by: subzero # Required-by: subzero
backports.functools-lru-cache==1.6.4 backports.functools-lru-cache==1.6.4
fcache==0.4.7 # https://github.com/tsroten/fcache/pull/34 and 35 fcache==0.5.0
json_tricks==3.16.1 json_tricks==3.16.1
tld==0.12.6 # https://github.com/barseghyanartur/tld/pull/119 tld==0.13
# Required-by: requests # Required-by: requests
certifi==2022.9.24 certifi==2022.9.24