python-slugify-4.0.0/0000755000175000017500000000000013553115747012631 5ustar hlehlepython-slugify-4.0.0/dev.requirements.txt0000644000175000017500000000002213553115747016664 0ustar hlehlepycodestyle==2.5.0python-slugify-4.0.0/LICENSE0000644000175000017500000000211713553115747013637 0ustar hlehleThe MIT License Copyright (c) Val Neekman @ Neekware Inc. http://neekware.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. python-slugify-4.0.0/slugify/0000755000175000017500000000000013553115747014313 5ustar hlehlepython-slugify-4.0.0/slugify/special.py0000644000175000017500000000222013553115747016301 0ustar hlehle# -*- coding: utf-8 -*- def add_uppercase_char(char_list): """ Given a replacement char list, this adds uppercase chars to the list """ for item in char_list: char, xlate = item upper_dict = char.upper(), xlate.capitalize() if upper_dict not in char_list and char != upper_dict[0]: char_list.insert(0, upper_dict) return char_list # Language specific pre translations # Source awesome-slugify _CYRILLIC = [ # package defaults: (u'ё', u'e'), # io / yo (u'я', u'ya'), # ia (u'х', u'h'), # kh (u'у', u'y'), # u (u'щ', u'sch'), # shch (u'ю', u'u'), # iu / yu ] CYRILLIC = add_uppercase_char(_CYRILLIC) _GERMAN = [ # package defaults: (u'ä', u'ae'), # a (u'ö', u'oe'), # o (u'ü', u'ue'), # u ] GERMAN = add_uppercase_char(_GERMAN) _GREEK = [ # package defaults: (u'χ', u'ch'), # kh (u'Ξ', u'X'), # Ks (u'ϒ', u'Y'), # U (u'υ', u'y'), # u (u'ύ', u'y'), (u'ϋ', u'y'), (u'ΰ', u'y'), ] GREEK = add_uppercase_char(_GREEK) # Pre translations PRE_TRANSLATIONS = CYRILLIC + GERMAN + GREEK python-slugify-4.0.0/slugify/slugify.py0000644000175000017500000001362513553115747016356 0ustar hlehleimport re import unicodedata import types import sys try: from htmlentitydefs import name2codepoint _unicode = unicode _unicode_type = types.UnicodeType except ImportError: from html.entities import name2codepoint _unicode = str _unicode_type = str unichr = chr try: import text_unidecode as unidecode except ImportError: import unidecode __all__ = ['slugify', 'smart_truncate'] CHAR_ENTITY_PATTERN = re.compile(r'&(%s);' % '|'.join(name2codepoint)) DECIMAL_PATTERN = re.compile(r'&#(\d+);') HEX_PATTERN = re.compile(r'&#x([\da-fA-F]+);') QUOTE_PATTERN = re.compile(r'[\']+') ALLOWED_CHARS_PATTERN = re.compile(r'[^-a-z0-9]+') ALLOWED_CHARS_PATTERN_WITH_UPPERCASE = re.compile(r'[^-a-zA-Z0-9]+') DUPLICATE_DASH_PATTERN = re.compile(r'-{2,}') NUMBERS_PATTERN = re.compile(r'(?<=\d),(?=\d)') DEFAULT_SEPARATOR = '-' def smart_truncate(string, max_length=0, word_boundary=False, separator=' ', save_order=False): """ Truncate a string. :param string (str): string for modification :param max_length (int): output string length :param word_boundary (bool): :param save_order (bool): if True then word order of output string is like input string :param separator (str): separator between words :return: """ string = string.strip(separator) if not max_length: return string if len(string) < max_length: return string if not word_boundary: return string[:max_length].strip(separator) if separator not in string: return string[:max_length] truncated = '' for word in string.split(separator): if word: next_len = len(truncated) + len(word) if next_len < max_length: truncated += '{}{}'.format(word, separator) elif next_len == max_length: truncated += '{}'.format(word) break else: if save_order: break if not truncated: # pragma: no cover truncated = string[:max_length] return truncated.strip(separator) def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, word_boundary=False, separator=DEFAULT_SEPARATOR, save_order=False, stopwords=(), regex_pattern=None, lowercase=True, replacements=()): """ Make a slug from the given text. :param text (str): initial text :param entities (bool): converts html entities to unicode :param decimal (bool): converts html decimal to unicode :param hexadecimal (bool): converts html hexadecimal to unicode :param max_length (int): output string length :param word_boundary (bool): truncates to complete word even if length ends up shorter than max_length :param save_order (bool): if parameter is True and max_length > 0 return whole words in the initial order :param separator (str): separator between words :param stopwords (iterable): words to discount :param regex_pattern (str): regex pattern for allowed characters :param lowercase (bool): activate case sensitivity by setting it to False :param replacements (iterable): list of replacement rules e.g. [['|', 'or'], ['%', 'percent']] :return (str): """ # user-specific replacements if replacements: for old, new in replacements: text = text.replace(old, new) # ensure text is unicode if not isinstance(text, _unicode_type): text = _unicode(text, 'utf-8', 'ignore') # replace quotes with dashes - pre-process text = QUOTE_PATTERN.sub(DEFAULT_SEPARATOR, text) # decode unicode text = unidecode.unidecode(text) # ensure text is still in unicode if not isinstance(text, _unicode_type): text = _unicode(text, 'utf-8', 'ignore') # character entity reference if entities: text = CHAR_ENTITY_PATTERN.sub(lambda m: unichr(name2codepoint[m.group(1)]), text) # decimal character reference if decimal: try: text = DECIMAL_PATTERN.sub(lambda m: unichr(int(m.group(1))), text) except Exception: pass # hexadecimal character reference if hexadecimal: try: text = HEX_PATTERN.sub(lambda m: unichr(int(m.group(1), 16)), text) except Exception: pass # translate text = unicodedata.normalize('NFKD', text) if sys.version_info < (3,): text = text.encode('ascii', 'ignore') # make the text lowercase (optional) if lowercase: text = text.lower() # remove generated quotes -- post-process text = QUOTE_PATTERN.sub('', text) # cleanup numbers text = NUMBERS_PATTERN.sub('', text) # replace all other unwanted characters if lowercase: pattern = regex_pattern or ALLOWED_CHARS_PATTERN else: pattern = regex_pattern or ALLOWED_CHARS_PATTERN_WITH_UPPERCASE text = re.sub(pattern, DEFAULT_SEPARATOR, text) # remove redundant text = DUPLICATE_DASH_PATTERN.sub(DEFAULT_SEPARATOR, text).strip(DEFAULT_SEPARATOR) # remove stopwords if stopwords: if lowercase: stopwords_lower = [s.lower() for s in stopwords] words = [w for w in text.split(DEFAULT_SEPARATOR) if w not in stopwords_lower] else: words = [w for w in text.split(DEFAULT_SEPARATOR) if w not in stopwords] text = DEFAULT_SEPARATOR.join(words) # finalize user-specific replacements if replacements: for old, new in replacements: text = text.replace(old, new) # smart truncate if requested if max_length > 0: text = smart_truncate(text, max_length, word_boundary, DEFAULT_SEPARATOR, save_order) if separator != DEFAULT_SEPARATOR: text = text.replace(DEFAULT_SEPARATOR, separator) return text def main(): # pragma: no cover if len(sys.argv) < 2: print("Usage %s TEXT TO SLUGIFY" % sys.argv[0]) else: text = ' '.join(sys.argv[1:]) print(slugify(text)) python-slugify-4.0.0/slugify/__init__.py0000644000175000017500000000031013553115747016416 0ustar hlehlefrom .special import * from .slugify import * __author__ = 'Val Neekman @ Neekware Inc. [@vneekman]' __description__ = 'A Python slugify application that also handles Unicode' __version__ = '4.0.0' python-slugify-4.0.0/.vscode/0000755000175000017500000000000013553115747014172 5ustar hlehlepython-slugify-4.0.0/.vscode/settings.json0000644000175000017500000000021413553115747016722 0ustar hlehle{ "python.linting.pylintEnabled": false, "restructuredtext.confPath": "", "python.pythonPath": "/usr/local/opt/python/bin/python3.6" }python-slugify-4.0.0/test.py0000644000175000017500000002014413553115747014163 0ustar hlehle# -*- coding: utf-8 -*- import unittest from slugify import slugify from slugify import smart_truncate class TestSlugification(unittest.TestCase): def test_extraneous_seperators(self): txt = "This is a test ---" r = slugify(txt) self.assertEqual(r, "this-is-a-test") txt = "___This is a test ---" r = slugify(txt) self.assertEqual(r, "this-is-a-test") txt = "___This is a test___" r = slugify(txt) self.assertEqual(r, "this-is-a-test") def test_non_word_characters(self): txt = "This -- is a ## test ---" r = slugify(txt) self.assertEqual(r, "this-is-a-test") def test_phonetic_conversion_of_eastern_scripts(self): txt = '影師嗎' r = slugify(txt) self.assertEqual(r, "ying-shi-ma") def test_accented_text(self): txt = 'C\'est déjà l\'été.' r = slugify(txt) self.assertEqual(r, "c-est-deja-l-ete") txt = 'Nín hǎo. Wǒ shì zhōng guó rén' r = slugify(txt) self.assertEqual(r, "nin-hao-wo-shi-zhong-guo-ren") def test_accented_text_with_non_word_characters(self): txt = 'jaja---lol-méméméoo--a' r = slugify(txt) self.assertEqual(r, "jaja-lol-mememeoo-a") def test_cyrillic_text(self): txt = 'Компьютер' r = slugify(txt) self.assertEqual(r, "kompiuter") def test_max_length(self): txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=9) self.assertEqual(r, "jaja-lol") txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=15) self.assertEqual(r, "jaja-lol-mememe") def test_max_length_cutoff_not_required(self): txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=50) self.assertEqual(r, "jaja-lol-mememeoo-a") def test_word_boundary(self): txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=15, word_boundary=True) self.assertEqual(r, "jaja-lol-a") txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=17, word_boundary=True) self.assertEqual(r, "jaja-lol-mememeoo") txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=18, word_boundary=True) self.assertEqual(r, "jaja-lol-mememeoo") txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=19, word_boundary=True) self.assertEqual(r, "jaja-lol-mememeoo-a") def test_custom_separator(self): txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=20, word_boundary=True, separator=".") self.assertEqual(r, "jaja.lol.mememeoo.a") def test_multi_character_separator(self): txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=20, word_boundary=True, separator="ZZZZZZ") self.assertEqual(r, "jajaZZZZZZlolZZZZZZmememeooZZZZZZa") def test_save_order(self): txt = 'one two three four five' r = slugify(txt, max_length=13, word_boundary=True, save_order=True) self.assertEqual(r, "one-two-three") txt = 'one two three four five' r = slugify(txt, max_length=13, word_boundary=True, save_order=False) self.assertEqual(r, "one-two-three") txt = 'one two three four five' r = slugify(txt, max_length=12, word_boundary=True, save_order=False) self.assertEqual(r, "one-two-four") txt = 'one two three four five' r = slugify(txt, max_length=12, word_boundary=True, save_order=True) self.assertEqual(r, "one-two") def test_stopword_removal(self): txt = 'this has a stopword' r = slugify(txt, stopwords=['stopword']) self.assertEqual(r, 'this-has-a') def test_stopword_removal_casesensitive(self): txt = 'thIs Has a stopword Stopword' r = slugify(txt, stopwords=['Stopword'], lowercase=False) self.assertEqual(r, 'thIs-Has-a-stopword') def test_multiple_stopword_occurances(self): txt = 'the quick brown fox jumps over the lazy dog' r = slugify(txt, stopwords=['the']) self.assertEqual(r, 'quick-brown-fox-jumps-over-lazy-dog') def test_differently_cased_stopword_match(self): txt = 'Foo A FOO B foo C' r = slugify(txt, stopwords=['foo']) self.assertEqual(r, 'a-b-c') txt = 'Foo A FOO B foo C' r = slugify(txt, stopwords=['FOO']) self.assertEqual(r, 'a-b-c') def test_multiple_stopwords(self): txt = 'the quick brown fox jumps over the lazy dog in a hurry' r = slugify(txt, stopwords=['the', 'in', 'a', 'hurry']) self.assertEqual(r, 'quick-brown-fox-jumps-over-lazy-dog') def test_stopwords_with_different_separator(self): txt = 'the quick brown fox jumps over the lazy dog' r = slugify(txt, stopwords=['the'], separator=' ') self.assertEqual(r, 'quick brown fox jumps over lazy dog') def test_html_entities_on(self): txt = 'foo & bar' r = slugify(txt) self.assertEqual(r, 'foo-bar') def test_html_entities_off(self): txt = 'foo & bar' r = slugify(txt, entities=False) self.assertEqual(r, 'foo-amp-bar') def test_html_decimal_on(self): txt = 'Ž' r = slugify(txt, decimal=True) self.assertEqual(r, 'z') def test_html_decimal_off(self): txt = 'Ž' r = slugify(txt, entities=False, decimal=False) self.assertEqual(r, '381') def test_html_hexadecimal_on(self): txt = 'Ž' r = slugify(txt, hexadecimal=True) self.assertEqual(r, 'z') def test_html_hexadecimal_off(self): txt = 'Ž' r = slugify(txt, hexadecimal=False) self.assertEqual(r, 'x17d') def test_starts_with_number(self): txt = '10 amazing secrets' r = slugify(txt) self.assertEqual(r, '10-amazing-secrets') def test_contains_numbers(self): txt = 'buildings with 1000 windows' r = slugify(txt) self.assertEqual(r, 'buildings-with-1000-windows') def test_ends_with_number(self): txt = 'recipe number 3' r = slugify(txt) self.assertEqual(r, 'recipe-number-3') def test_numbers_only(self): txt = '404' r = slugify(txt) self.assertEqual(r, '404') def test_numbers_and_symbols(self): txt = '1,000 reasons you are #1' r = slugify(txt) self.assertEqual(r, '1000-reasons-you-are-1') def test_regex_pattern_keep_underscore(self): txt = "___This is a test___" regex_pattern = r'[^-a-z0-9_]+' r = slugify(txt, regex_pattern=regex_pattern) self.assertEqual(r, "___this-is-a-test___") def test_regex_pattern_keep_underscore_with_underscore_as_separator(self): """ The regex_pattern turns the power to the caller. Hence the caller must ensure that a custom separator doesn't clash with the regex_pattern. """ txt = "___This is a test___" regex_pattern = r'[^-a-z0-9_]+' r = slugify(txt, separator='_', regex_pattern=regex_pattern) self.assertNotEqual(r, "_this_is_a_test_") def test_replacements(self): txt = '10 | 20 %' r = slugify(txt, replacements=[['|', 'or'], ['%', 'percent']]) self.assertEqual(r, "10-or-20-percent") txt = 'I ♥ 🦄' r = slugify(txt, replacements=[['♥', 'amour'], ['🦄', 'licorne']]) self.assertEqual(r, "i-amour-licorne") def test_replacements_german_umlaut_custom(self): txt = 'ÜBER Über German Umlaut' r = slugify(txt, replacements=[['Ü', 'UE'], ['ü', 'ue']]) self.assertEqual(r, "ueber-ueber-german-umlaut") class TestUtils(unittest.TestCase): def test_smart_truncate_no_max_length(self): txt = '1,000 reasons you are #1' r = smart_truncate(txt) self.assertEqual(r, txt) def test_smart_truncate_no_seperator(self): txt = '1,000 reasons you are #1' r = smart_truncate(txt, max_length=100, separator='_') self.assertEqual(r, txt) if __name__ == '__main__': unittest.main() python-slugify-4.0.0/CHANGELOG.md0000644000175000017500000000550613553115747014450 0ustar hlehle## 4.0.0 - Drop support from 2.6, & < 3.4.5 ## 3.0.6 - Fixed encoding in special.py ## 3.0.5 - Add test for pre-translation (e.g German Umlaut) - Add special char supports (optional Use) ## 3.0.4 - Now supporting text-unidecode>=1.3 - Now supporting Unidecode>=1.1.1 ## 3.0.3 - Remove unicode chars from file ## 3.0.2 - Add official support of Py 3.7 ## 3.0.1 - Add test.py to manifest ## 3.0.0 - Upgrade Unidecode - Promote text-unidecode as the primary decoding package - Add Unidecode as an optional extra. "pip install python-slugify[unidecode]" ## 2.0.1 - Add replacements option e.g. [['|', 'or'], ['%', 'percent'], ['-', '_']] (@andriyor) ## 2.0.0 - Fix alternative dependency installation ## 1.2.6 - Add support for case sensitive slugs (@s-m-e) ## 1.2.5 - Add support for using text-unidecode (@bolkedebruin) - Switch to pycodestyle instead of pep8 ## 1.2.4 - Remove build artifacts during packaging - Simplify the setup.py file (@reece) ## 1.2.3 - Republish - possible corrupt 1.2.2 build ## 1.2.2 - Add `regex_pattern` option. (@vrbaskiz) - Add Python 3.6 support ## 1.2.1 - Including certain files (e.g. license.md) in sdists via MANIFEST.in (@proinsias) - Relax licensing by moving from BSD to MIT - Add Python 3.5 support - Add more tests ## 1.2.0 Backward incompatible change: (@fabiocaccamo) - In version < 1.2.0 all single quotes ( ' ) were removed, and moving forward, >= 1.2.0, they will be replaced with ( - ). Example: < 1.2.0 -- ('C\'est déjà l\'été.' -> "cest-deja-lete") >= 1.2.0 -- ('C\'est déjà l\'été.' -> "c-est-deja-l-ete") ## 1.1.4 Bugfix: - Add more test cases, dropped `official` support for python 3.2 ## 1.1.3 Bugfix: - Handle unichar in python 3.x ## 1.1.2 Enhancement: - Ability to remove `stopwords` from string ## 1.0.2 Enhancement: - A new PyPI release ## 1.0.1 Enhancement: - Promoting to production grade ## 0.1.1 Enhancement: - Added option to save word order - Removed 2to3 dependency - Added more tests ## 0.1.0 Enhancement: - Added more test - Added test for python 3.4 ## 0.0.9 Enhancement: - Enable console_scripts ## 0.0.8 Enhancement: - Move logic out of __init__.py - Added console_scripts (@ekamil) - Updated pep8.sh - Added pypy support ## 0.0.7 Enhancement: - Handle encoding in setup file - Update ReadME, ChangeLog, License files ## 0.0.6 Enhancement: - Update for smart_truncate ## 0.0.5 Features: - Added Python 3.2 and 3.3 support (work by: arthurdarcet@github) ## 0.0.4 Features: - Added option to choose non-dash separators (request by: danilodimoia@github) ## 0.0.3 Features: - Added the ability to truncate slugs (request by: juanriaza@github) ## 0.0.2 Enhancement: - Incremental update ## 0.0.1 - Initial version python-slugify-4.0.0/README.md0000644000175000017500000001233713553115747014116 0ustar hlehlePython Slugify ==================== **A Python slugify application that handles unicode**. [![status-image]][status-link] [![version-image]][version-link] [![coverage-image]][coverage-link] Overview ==================== **Best attempt** to create slugs from unicode strings while keeping it **DRY**. Notice ==================== This module, by default installs and uses [text-unidecode](https://github.com/kmike/text-unidecode) *(GPL & Perl Artistic)* for its decoding needs. 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. How to install ==================== easy_install python-slugify |OR| easy_install python-slugify[unidecode] -- OR -- pip install python-slugify |OR| pip install python-slugify[unidecode] Options =================== ```python def slugify( text, entities=True, decimal=True, hexadecimal=True, max_length=0, word_boundary=False, separator='-', save_order=False, stopwords=(), regex_pattern=None, lowercase=True, replacements=() ): """ Make a slug from the given text. :param text (str): initial text :param entities (bool): converts html entities to unicode (foo & bar -> foo-bar) :param decimal (bool): converts html decimal to unicode (Ž -> Ž -> z) :param hexadecimal (bool): converts html hexadecimal to unicode (Ž -> Ž -> z) :param max_length (int): output string length :param word_boundary (bool): truncates to end of full words (length may be shorter than max_length) :param save_order (bool): if parameter is True and max_length > 0 return whole words in the initial order :param separator (str): separator between words :param stopwords (iterable): words to discount :param regex_pattern (str): regex pattern for allowed characters :param lowercase (bool): activate case sensitivity by setting it to False :param replacements (iterable): list of replacement rules e.g. [['|', 'or'], ['%', 'percent']] :return (str): slugify text """ ``` How to use ==================== ```python from slugify import slugify txt = "This is a test ---" r = slugify(txt) self.assertEqual(r, "this-is-a-test") txt = '影師嗎' r = slugify(txt) self.assertEqual(r, "ying-shi-ma") txt = 'C\'est déjà l\'été.' r = slugify(txt) self.assertEqual(r, "c-est-deja-l-ete") txt = 'Nín hǎo. Wǒ shì zhōng guó rén' r = slugify(txt) self.assertEqual(r, "nin-hao-wo-shi-zhong-guo-ren") txt = 'Компьютер' r = slugify(txt) self.assertEqual(r, "kompiuter") txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=9) self.assertEqual(r, "jaja-lol") txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=15, word_boundary=True) self.assertEqual(r, "jaja-lol-a") txt = 'jaja---lol-méméméoo--a' r = slugify(txt, max_length=20, word_boundary=True, separator=".") self.assertEqual(r, "jaja.lol.mememeoo.a") txt = 'one two three four five' r = slugify(txt, max_length=13, word_boundary=True, save_order=True) self.assertEqual(r, "one-two-three") txt = 'the quick brown fox jumps over the lazy dog' r = slugify(txt, stopwords=['the']) self.assertEqual(r, 'quick-brown-fox-jumps-over-lazy-dog') txt = 'the quick brown fox jumps over the lazy dog in a hurry' r = slugify(txt, stopwords=['the', 'in', 'a', 'hurry']) self.assertEqual(r, 'quick-brown-fox-jumps-over-lazy-dog') txt = 'thIs Has a stopword Stopword' r = slugify(txt, stopwords=['Stopword'], lowercase=False) self.assertEqual(r, 'thIs-Has-a-stopword') txt = "___This is a test___" regex_pattern = r'[^-a-z0-9_]+' r = slugify(txt, regex_pattern=regex_pattern) self.assertEqual(r, "___this-is-a-test___") txt = "___This is a test___" regex_pattern = r'[^-a-z0-9_]+' r = slugify(txt, separator='_', regex_pattern=regex_pattern) self.assertNotEqual(r, "_this_is_a_test_") txt = '10 | 20 %' r = slugify(txt, replacements=[['|', 'or'], ['%', 'percent']]) self.assertEqual(r, "10-or-20-percent") txt = 'ÜBER Über German Umlaut' r = slugify(txt, replacements=[['Ü', 'UE'], ['ü', 'ue']]) self.assertEqual(r, "ueber-ueber-german-umlaut") ``` For more examples, have a look at the [test.py](test.py) file. Running the tests ==================== To run the tests against the current environment: python test.py License ==================== Released under a ([MIT](LICENSE)) license. Version ==================== X.Y.Z Version `MAJOR` version -- when you make incompatible API changes, `MINOR` version -- when you add functionality in a backwards-compatible manner, and `PATCH` version -- when you make backwards-compatible bug fixes. [status-image]: https://secure.travis-ci.org/un33k/python-slugify.png?branch=master [status-link]: http://travis-ci.org/un33k/python-slugify?branch=master [version-image]: https://img.shields.io/pypi/v/python-slugify.svg [version-link]: https://pypi.python.org/pypi/python-slugify [coverage-image]: https://coveralls.io/repos/un33k/python-slugify/badge.svg [coverage-link]: https://coveralls.io/r/un33k/python-slugify [download-image]: https://img.shields.io/pypi/dm/python-slugify.svg [download-link]: https://pypi.python.org/pypi/python-slugify Sponsors ==================== [Surge](https://github.com/surgeforward) python-slugify-4.0.0/.gitignore0000644000175000017500000000134313553115747014622 0ustar hlehle# JebBrains IDE .idea/ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ *.*DS_Store python-slugify-4.0.0/format.sh0000755000175000017500000000064213553115747014462 0ustar hlehle#!/bin/bash # Ignoring autogenerated files # -- Migration directories # Ignoring error codes # -- E128 continuation line under-indented for visual indent # -- E261 at least two spaces before inline comment # -- E225 missing whitespace around operator # -- E501 line too long # Ignoring warning codes # -- W605 invalid escape sequence '\d' pycodestyle --ignore=E128,E261,E225,E501,W605 slugify test.py setup.py python-slugify-4.0.0/.travis.yml0000644000175000017500000000073613553115747014750 0ustar hlehlelanguage: python dist: xenial python: - "2.7" - "3.5" - "3.6" - "3.7" - "3.8-dev" - "pypy" - "pypy3" install: - pip install pip -U - pip install -e . - pip install pycodestyle - pip install coveralls - pip install https://github.com/un33k/pyflakes/tarball/master before_script: - "bash format.sh" - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pyflakes -x W slugify; fi script: coverage run --source=slugify test.py after_success: coveralls python-slugify-4.0.0/MANIFEST.in0000644000175000017500000000010713553115747014365 0ustar hlehleinclude CHANGELOG.md include LICENSE include README.md include test.py python-slugify-4.0.0/setup.py0000755000175000017500000000425313553115747014352 0ustar hlehle#!/usr/bin/env python # -*- coding: utf-8 -*- from setuptools import setup, find_packages import re import os import sys import codecs name = 'python-slugify' package = 'slugify' description = 'A Python Slugify application that handles Unicode' url = 'https://github.com/un33k/python-slugify' author = 'Val Neekman' author_email = 'info@neekware.com' license = 'MIT' install_requires = ['text-unidecode>=1.3'] extras_require = {'unidecode': ['Unidecode>=1.1.1']} classifiers = [ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Topic :: Software Development :: Build Tools', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', ] def get_version(package): """ Return package version as listed in `__version__` in `init.py`. """ init_py = codecs.open(os.path.join(package, '__init__.py'), encoding='utf-8').read() return re.search("^__version__ = ['\"]([^'\"]+)['\"]", init_py, re.MULTILINE).group(1) if sys.argv[-1] == 'build': os.system("python setup.py sdist bdist_wheel") if sys.argv[-1] == 'publish': os.system("python setup.py sdist upload") args = {'version': get_version(package)} print("You probably want to also tag the version now:") print(" git tag -a %(version)s -m 'version %(version)s' && git push --tags" % args) sys.exit() EXCLUDE_FROM_PACKAGES = [] setup( name=name, version=get_version(package), url=url, license=license, description=description, long_description=description, author=author, author_email=author_email, packages=find_packages(exclude=EXCLUDE_FROM_PACKAGES), install_requires=install_requires, extras_require=extras_require, python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*', classifiers=classifiers, entry_points={'console_scripts': ['slugify=slugify.slugify:main']}, ) python-slugify-4.0.0/setup.cfg0000644000175000017500000000003213553115747014445 0ustar hlehle[bdist_wheel] universal=1