Browse Source

setup: drop iso-639/iso3166, default to pycountry

- drop iso-639 and iso3166 dependencies in favor of pycountry
- remove the `STREAMLINK_USE_PYCOUNTRY` env var switch from setup.py
- move dependencies list from setup.py to setup.cfg
- update utils.l10n and its tests
- update Windows installer config
- update docs
pull/4176/head
bastimeyer 2 months ago
committed by Forrest
parent
commit
aabd6af718
  1. 14
      docs/install.rst
  2. 1
      script/install-dependencies.sh
  3. 3
      script/makeinstaller.sh
  4. 8
      setup.cfg
  5. 20
      setup.py
  6. 80
      src/streamlink/utils/l10n.py
  7. 40
      tests/utils/test_l10n.py

14
docs/install.rst

@ -284,10 +284,9 @@ Name Notes
**Automatically installed by the setup script**
--------------------------------------------------------------------------------
`iso-639`_ Used for localization settings, provides language information
`iso3166`_ Used for localization settings, provides country information
`isodate`_ Used for parsing ISO8601 strings
`lxml`_ Used for processing HTML and XML data
`pycountry`_ Used for localization settings, provides country and language data
`pycryptodome`_ Used for decrypting encrypted streams
`PySocks`_ Used for SOCKS Proxies
`requests`_ Used for making any kind of HTTP/HTTPS request
@ -301,20 +300,9 @@ Name Notes
- HLS streams optionally need to get remuxed depending on the stream selection.
==================================== ===========================================
Alternative dependencies
^^^^^^^^^^^^^^^^^^^^^^^^
With this environment variable it is possible to use `pycountry`_ instead of `iso-639`_ and `iso3166`_.
.. code-block:: console
$ export STREAMLINK_USE_PYCOUNTRY="true"
.. _Python: https://www.python.org/
.. _python-setuptools: https://setuptools.pypa.io/en/latest/
.. _iso-639: https://pypi.org/project/iso-639/
.. _iso3166: https://pypi.org/project/iso3166/
.. _isodate: https://pypi.org/project/isodate/
.. _lxml: https://lxml.de/
.. _pycountry: https://pypi.org/project/pycountry/

1
script/install-dependencies.sh

@ -6,7 +6,6 @@ set -ex
python -m pip install --disable-pip-version-check --upgrade pip setuptools
python -m pip install --upgrade -r dev-requirements.txt
python -m pip install pycountry
# https://github.com/streamlink/streamlink/issues/4021
python -m pip install brotli
python -m pip install -e .

3
script/makeinstaller.sh

@ -89,11 +89,10 @@ format=bundled
[Include]
packages=pkg_resources
iso639
pycountry
pypi_wheels=certifi==2021.5.30
charset-normalizer==2.0.4
idna==3.2
iso3166==1.0.1
isodate==0.6.0
lxml==4.6.4
pycryptodome==3.10.1

8
setup.cfg

@ -38,6 +38,14 @@ python_requires = >=3.6, <4
package_dir =
=src
packages = find:
install_requires =
isodate
lxml >=4.6.4,<5.0
pycountry
pycryptodome >=3.4.3,<4
PySocks !=1.5.7,>=1.5.6
requests >=2.26.0,<3.0
websocket-client >=1.2.1,<2.0
[options.packages.find]
where = src

20
setup.py

@ -1,5 +1,5 @@
#!/usr/bin/env python
from os import environ, path
from os import path
from sys import argv, exit, version_info
from textwrap import dedent
@ -38,23 +38,6 @@ if "test" in argv:
"""))
deps = [
"isodate",
"lxml>=4.6.4,<5.0",
"pycryptodome>=3.4.3,<4",
"PySocks!=1.5.7,>=1.5.6",
"requests>=2.26.0,<3.0",
"websocket-client>=1.2.1,<2.0",
]
# for localization
if environ.get("STREAMLINK_USE_PYCOUNTRY"):
deps.append("pycountry")
else:
deps.append("iso-639")
deps.append("iso3166")
def is_wheel_for_windows():
if "bdist_wheel" in argv:
names = ["win32", "win-amd64", "cygwin"]
@ -96,7 +79,6 @@ data_files = [
setup(
version=versioneer.get_version(),
cmdclass=versioneer.get_cmdclass(),
install_requires=deps,
entry_points=entry_points,
data_files=data_files,
)

80
src/streamlink/utils/l10n.py

@ -1,20 +1,11 @@
import locale
import logging
try:
from iso639 import languages
from iso3166 import countries
PYCOUNTRY = False
except ImportError: # pragma: no cover
from pycountry import languages, countries
PYCOUNTRY = True
from pycountry import countries, languages
DEFAULT_LANGUAGE = "en"
DEFAULT_COUNTRY = "US"
DEFAULT_LANGUAGE_CODE = "{0}_{1}".format(DEFAULT_LANGUAGE, DEFAULT_COUNTRY)
DEFAULT_LANGUAGE_CODE = f"{DEFAULT_LANGUAGE}_{DEFAULT_COUNTRY}"
log = logging.getLogger(__name__)
@ -30,14 +21,16 @@ class Country:
@classmethod
def get(cls, country):
try:
if PYCOUNTRY:
c = countries.lookup(country)
return Country(c.alpha_2, c.alpha_3, c.numeric, c.name, getattr(c, "official_name", c.name))
else:
c = countries.get(country)
return Country(c.alpha2, c.alpha3, c.numeric, c.name, c.apolitical_name)
c = countries.lookup(country)
return Country(
c.alpha_2,
c.alpha_3,
c.numeric,
c.name,
getattr(c, "official_name", c.name)
)
except (LookupError, KeyError):
raise LookupError("Invalid country code: {0}".format(country))
raise LookupError(f"Invalid country code: {country}")
def __eq__(self, other):
return (
@ -66,38 +59,23 @@ class Language:
@classmethod
def get(cls, language):
try:
if PYCOUNTRY:
lang = (languages.get(alpha_2=language)
or languages.get(alpha_3=language)
or languages.get(bibliographic=language)
or languages.get(name=language))
if not lang:
raise KeyError(language)
return Language(
# some languages don't have an alpha_2 code
getattr(lang, "alpha_2", ""),
lang.alpha_3,
lang.name,
getattr(lang, "bibliographic", "")
)
else:
lang = None
if len(language) == 2:
lang = languages.get(alpha2=language)
elif len(language) == 3:
for code_type in ['part2b', 'part2t', 'part3']:
try:
lang = languages.get(**{code_type: language})
break
except KeyError:
pass
if not lang:
raise KeyError(language)
else:
raise KeyError(language)
return Language(lang.alpha2, lang.part3, lang.name, lang.part2b or lang.part2t)
lang = (
languages.get(alpha_2=language)
or languages.get(alpha_3=language)
or languages.get(bibliographic=language)
or languages.get(name=language)
)
if not lang:
raise KeyError(language)
return Language(
# some languages don't have an alpha_2 code
getattr(lang, "alpha_2", ""),
lang.alpha_3,
lang.name,
getattr(lang, "bibliographic", "")
)
except (LookupError, KeyError):
raise LookupError("Invalid language code: {0}".format(language))
raise LookupError(f"Invalid language code: {language}")
def __eq__(self, other):
return (
@ -130,7 +108,7 @@ class Localization:
def _parse_locale_code(self, language_code):
parts = language_code.split("_", 1)
if len(parts) != 2 or len(parts[0]) != 2 or len(parts[1]) != 2:
raise LookupError("Invalid language code: {0}".format(language_code))
raise LookupError(f"Invalid language code: {language_code}")
return self.get_language(parts[0]), self.get_country(parts[1])
@language_code.setter
@ -156,7 +134,7 @@ class Localization:
self._language_code = DEFAULT_LANGUAGE_CODE
else:
raise
log.debug("Language code: {0}".format(self._language_code))
log.debug(f"Language code: {self._language_code}")
def equivalent(self, language=None, country=None):
equivalent = True

40
tests/utils/test_l10n.py

@ -3,23 +3,8 @@ from unittest.mock import patch
import streamlink.utils.l10n as l10n
try:
import iso639 # noqa: F401
import iso3166 # noqa: F401
ISO639 = True
except ImportError: # pragma: no cover
ISO639 = False
try:
import pycountry # noqa: F401
PYCOUNTRY = True
except ImportError: # pragma: no cover
PYCOUNTRY = False
class LocalizationTestsMixin:
class TestLocalization(unittest.TestCase):
def test_language_code_us(self):
locale = l10n.Localization("en_US")
self.assertEqual("en_US", locale.language_code)
@ -117,29 +102,6 @@ class LocalizationTestsMixin:
self.assertEqual(a.name, "Desano")
self.assertEqual(a.bibliographic, "")
@unittest.skipIf(not ISO639, "iso639+iso3166 modules are required to test iso639+iso3166 Localization")
class TestLocalization(LocalizationTestsMixin, unittest.TestCase):
def setUp(self):
l10n.PYCOUNTRY = False
def test_pycountry(self):
self.assertEqual(False, l10n.PYCOUNTRY)
@unittest.skipIf(not PYCOUNTRY, "pycountry module required to test pycountry Localization")
class TestLocalizationPyCountry(LocalizationTestsMixin, unittest.TestCase):
"""Duplicate of all the Localization tests but using PyCountry instead of the iso* modules"""
def setUp(self):
from pycountry import languages, countries
l10n.countries = countries
l10n.languages = languages
l10n.PYCOUNTRY = True
def test_pycountry(self):
self.assertEqual(True, l10n.PYCOUNTRY)
# issue #3057: generic "en" lookups via pycountry yield the "En" language, but not "English"
def test_language_en(self):
english_a = l10n.Localization.get_language("en")

Loading…
Cancel
Save