From 2c704c998a5c0d9873eea8f43be644899070f2fb Mon Sep 17 00:00:00 2001 From: Nathan Drezner Date: Thu, 20 Nov 2025 17:49:55 -0500 Subject: [PATCH 1/2] Use anyascii (ISC licensed) as default decoder --- README.md | 26 ++++++++++++++++++-------- setup.py | 7 +++++-- slugify/slugify.py | 21 ++++++++++++++++++--- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b0100f2..bf1fba8 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,13 @@ # Notice -This module, by default installs and uses [text-unidecode](https://github.com/kmike/text-unidecode) _(GPL & Perl Artistic)_ for its decoding needs. +This module, by default installs and uses [anyascii](https://github.com/anyascii/anyascii) _(ISC License)_ for its decoding needs. This is a permissive, non-GPL alternative that works well for most use cases. -However, there is an alternative decoding package called [Unidecode](https://github.com/avian2/unidecode) _(GPL)_. It can be installed as `python-slugify[unidecode]` for those who prefer it. `Unidecode` is believed to be more [advanced](https://github.com/un33k/python-slugify/wiki/Python-Slugify-Wiki#notes-on-unidecode). +However, there are alternative decoding packages available: +- [Unidecode](https://github.com/avian2/unidecode) _(GPL)_ - Can be installed as `python-slugify[unidecode]`. Believed to be more [advanced](https://github.com/un33k/python-slugify/wiki/Python-Slugify-Wiki#notes-on-unidecode). +- [text-unidecode](https://github.com/kmike/text-unidecode) _(GPL & Perl Artistic)_ - Can be installed as `python-slugify[text-unidecode]`. + +The library will automatically use `unidecode` or `text-unidecode` (in that order) if either is installed, falling back to `anyascii` if neither is available. ### `Official` Support Matrix @@ -26,11 +30,16 @@ However, there is an alternative decoding package called [Unidecode](https://git # How to install - pip install python-slugify +```bash +# Default installation (uses anyascii - non-GPL) +pip install python-slugify - # OR +# With optional GPL-licensed Unidecode (more advanced) +pip install python-slugify[unidecode] - pip install python-slugify[unidecode] +# With optional text-unidecode (GPL or Perl Artistic) +pip install python-slugify[text-unidecode] +``` # Options @@ -199,9 +208,10 @@ Please read the ([wiki](https://github.com/un33k/python-slugify/wiki/Python-Slug Released under a ([MIT](LICENSE)) license. -### Notes on GPL dependencies -Though the dependencies may be GPL licensed, `python-slugify` itself is not considered a derivative work and will remain under the MIT license. -If you wish to avoid installation of any GPL licensed packages, please note that the default dependency `text-unidecode` explicitly lets you choose to use the [Artistic License](https://opensource.org/license/artistic-perl-1-0-2/) instead. Use without concern. +### Notes on dependencies +The default dependency, `anyascii`, uses the permissive ISC License (similar to MIT), so there are no licensing concerns. + +If you choose to install the optional GPL-licensed packages (`unidecode` or `text-unidecode`), please note that `python-slugify` itself is not considered a derivative work and will remain under the MIT license. # Version diff --git a/setup.py b/setup.py index 293ea26..f098cbc 100755 --- a/setup.py +++ b/setup.py @@ -12,8 +12,11 @@ python_requires = ">=3.7" here = os.path.abspath(os.path.dirname(__file__)) -install_requires = ['text-unidecode>=1.3'] -extras_requires = {'unidecode': ['Unidecode>=1.1.1']} +install_requires = ['anyascii>=0.3.0'] +extras_requires = { + 'unidecode': ['Unidecode>=1.1.1'], + 'text-unidecode': ['text-unidecode>=1.3'] +} test_requires = [] about = {} diff --git a/slugify/slugify.py b/slugify/slugify.py index 67b31c8..2241cfe 100644 --- a/slugify/slugify.py +++ b/slugify/slugify.py @@ -7,10 +7,25 @@ try: import unidecode + _DECODER = 'unidecode' except ImportError: - import text_unidecode as unidecode - -__all__ = ['slugify', 'smart_truncate'] + try: + import text_unidecode as unidecode + _DECODER = 'text_unidecode' + except ImportError: + from anyascii import anyascii + # Create a wrapper module to match unidecode's API + class _AnyasciiWrapper: + @staticmethod + def unidecode(text): + return anyascii(text) + unidecode = _AnyasciiWrapper() + _DECODER = 'anyascii' + +__all__ = ['slugify', 'smart_truncate', 'SLUGIFY_DECODER'] + +# Export the decoder being used for user reference +SLUGIFY_DECODER = _DECODER CHAR_ENTITY_PATTERN = re.compile(r'&(%s);' % '|'.join(name2codepoint)) From c1dade63954885f1409ee40596645ee5eb8c3c34 Mon Sep 17 00:00:00 2001 From: Nathan Drezner Date: Thu, 20 Nov 2025 17:50:05 -0500 Subject: [PATCH 2/2] Add exceptions for anyascii --- test.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test.py b/test.py index d13ef94..d2f276b 100644 --- a/test.py +++ b/test.py @@ -7,6 +7,7 @@ from slugify import PRE_TRANSLATIONS from slugify import slugify from slugify import smart_truncate +from slugify import SLUGIFY_DECODER from slugify.__main__ import slugify_params, parse_args @@ -34,7 +35,11 @@ def test_non_word_characters(self): def test_phonetic_conversion_of_eastern_scripts(self): txt = '影師嗎' r = slugify(txt) - self.assertEqual(r, "ying-shi-ma") + # anyascii produces different (but valid) output + if SLUGIFY_DECODER == 'anyascii': + self.assertEqual(r, "yingshima") + else: + self.assertEqual(r, "ying-shi-ma") def test_accented_text(self): txt = '𝐚́́𝕒́àáâäãąā' @@ -57,7 +62,11 @@ def test_accented_text_with_non_word_characters(self): def test_cyrillic_text(self): txt = 'Компьютер' r = slugify(txt) - self.assertEqual(r, "kompiuter") + # anyascii produces different (but valid) output + if SLUGIFY_DECODER == 'anyascii': + self.assertEqual(r, "kompyuter") + else: + self.assertEqual(r, "kompiuter") def test_max_length(self): txt = 'jaja---lol-méméméoo--a'