From 1097c2366ffcb4356ba84f74c7daf42bcfe720bf Mon Sep 17 00:00:00 2001 From: Fahrzin Hemmati Date: Mon, 10 May 2021 17:44:12 -0700 Subject: [PATCH 01/33] Add better typing for slugify.slugify Currently, mypy understands the type as `Iterable[str]`, which doesn't match what should actually be passed in, which is `Iterable[Iterable[str]]` or, ideally, `Iterable[Tuple[str, str]]` --- slugify/slugify.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/slugify/slugify.py b/slugify/slugify.py index bb3aa95..2097c41 100644 --- a/slugify/slugify.py +++ b/slugify/slugify.py @@ -1,5 +1,6 @@ import re import unicodedata +import typing import types import sys @@ -76,7 +77,7 @@ def smart_truncate(string, max_length=0, word_boundary=False, separator=' ', sav 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=()): + replacements: typing.Iterable[typing.Iterable[str]]=()): """ Make a slug from the given text. :param text (str): initial text From a90d96778a004a9f2fdc232debcd72fdb5bbf085 Mon Sep 17 00:00:00 2001 From: Fahrzin Hemmati Date: Mon, 10 May 2021 17:52:09 -0700 Subject: [PATCH 02/33] whitespace around = --- slugify/slugify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slugify/slugify.py b/slugify/slugify.py index 2097c41..6d4a67e 100644 --- a/slugify/slugify.py +++ b/slugify/slugify.py @@ -77,7 +77,7 @@ def smart_truncate(string, max_length=0, word_boundary=False, separator=' ', sav 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: typing.Iterable[typing.Iterable[str]]=()): + replacements: typing.Iterable[typing.Iterable[str]] = ()): """ Make a slug from the given text. :param text (str): initial text From 45e0ae3d0db0fce0c7e875491848a646f3011386 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 15:30:11 -0500 Subject: [PATCH 03/33] github actions --- .github/workflows/ci.yml | 42 +++++++++++++++++++++++++++++++++++++ .github/workflows/dev.yml | 43 ++++++++++++++++++++++++++++++++++++++ .github/workflows/main.yml | 42 +++++++++++++++++++++++++++++++++++++ setup.py | 1 + 4 files changed, 128 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/dev.yml create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..34ea50c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: (CI) + +on: + push: + branches: + - ci + +jobs: + build: + name: Setup ${{ matrix.python }} ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + steps: + - name: setup-python ${{ matrix.python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + pip install coveralls --upgrade + - name: Run flake8 + run: | + pip install flake8 --upgrade + flake8 --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + - name: Run pycodestyle + run: | + pip install pycodestyle --upgrade + pycodestyle --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + - name: Run test + run: | + coverage run --source=python test.py + - name: Coveralls + run: coveralls --service=github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml new file mode 100644 index 0000000..ebfbe0a --- /dev/null +++ b/.github/workflows/dev.yml @@ -0,0 +1,43 @@ +name: (CI) + +on: + push: + branches: + - sandbox + - dev + +jobs: + build: + name: Setup ${{ matrix.python }} ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + steps: + - name: setup-python ${{ matrix.python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + pip install coveralls --upgrade + - name: Run flake8 + run: | + pip install flake8 --upgrade + flake8 --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + - name: Run pycodestyle + run: | + pip install pycodestyle --upgrade + pycodestyle --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + - name: Run test + run: | + coverage run --source=python test.py + - name: Coveralls + run: coveralls --service=github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..7b7146b --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,42 @@ +name: (CI) + +on: + push: + branches: + - master + +jobs: + build: + name: Setup ${{ matrix.python }} ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + steps: + - name: setup-python ${{ matrix.python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + pip install coveralls --upgrade + - name: Run flake8 + run: | + pip install flake8 --upgrade + flake8 --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + - name: Run pycodestyle + run: | + pip install pycodestyle --upgrade + pycodestyle --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + - name: Run test + run: | + coverage run --source=python test.py + - name: Coveralls + run: coveralls --service=github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/setup.py b/setup.py index 51b267f..769f5a1 100755 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', ] From 6404c1d5c66ac436ccc613eec338d4f548c00457 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 15:34:54 -0500 Subject: [PATCH 04/33] simplify action --- .github/workflows/dev.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index ebfbe0a..175facb 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -15,12 +15,13 @@ jobs: matrix: os: [macos-latest, windows-latest, ubuntu-latest] python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + steps: - - name: setup-python ${{ matrix.python }} + - uses: actions/checkout@v2 + - name: setup python uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} - - name: Install dependencies run: | python -m pip install --upgrade pip From 9b9b68f512edecf209d9af977d2080194ff55d88 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 15:38:40 -0500 Subject: [PATCH 05/33] git actions --- .github/workflows/dev.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 175facb..91929f7 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -1,5 +1,7 @@ -name: (CI) +name: DEV +# Run on push only for dev/sandbox +# Otherwise it may trigger concurrently `push & pull_request` on PRs. on: push: branches: @@ -8,12 +10,10 @@ on: jobs: build: - name: Setup ${{ matrix.python }} ${{ matrix.os }} - runs-on: ${{ matrix.os }} + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest strategy: - fail-fast: false matrix: - os: [macos-latest, windows-latest, ubuntu-latest] python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] steps: From ad33581c491fd16cd64930fe02c862c04595550b Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 16:11:08 -0500 Subject: [PATCH 06/33] version file, github action --- .github/workflows/dev.yml | 4 ++-- .vscode/settings.json | 3 ++- dev.requirements.txt | 3 ++- setup.py | 2 +- slugify/__init__.py | 5 ----- slugify/__main__.py | 4 ++-- slugify/__version__.py | 3 +++ slugify/slugify.py | 28 +++++++++------------------- slugify/special.py | 2 +- test.py | 1 - 10 files changed, 22 insertions(+), 33 deletions(-) create mode 100644 slugify/__version__.py diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 91929f7..fc6d0a5 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -30,11 +30,11 @@ jobs: - name: Run flake8 run: | pip install flake8 --upgrade - flake8 --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + flake8 --exclude=build --ignore=E501,F403,F401,E241,E225,E128 . - name: Run pycodestyle run: | pip install pycodestyle --upgrade - pycodestyle --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + pycodestyle --ignore=E128,E261,E225,E501,W605 slugify test.py setup.py - name: Run test run: | coverage run --source=python test.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 2ab09c1..ecfbb80 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "python.linting.pylintEnabled": false, "python.pythonPath": "/usr/bin/python3", -} \ No newline at end of file + "cSpell.words": ["Neekman", "shch", "xlate"] +} diff --git a/dev.requirements.txt b/dev.requirements.txt index 337aa36..2b4e781 100644 --- a/dev.requirements.txt +++ b/dev.requirements.txt @@ -1,2 +1,3 @@ pycodestyle==2.7.0 -twine==3.4.1 \ No newline at end of file +twine==3.4.1 +flake8==4.0.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 769f5a1..8adc159 100755 --- a/setup.py +++ b/setup.py @@ -37,7 +37,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() + init_py = codecs.open(os.path.join(package, '__version__.py'), encoding='utf-8').read() return re.search("^__version__ = ['\"]([^'\"]+)['\"]", init_py, re.MULTILINE).group(1) diff --git a/slugify/__init__.py b/slugify/__init__.py index 6c59f4e..ac21492 100644 --- a/slugify/__init__.py +++ b/slugify/__init__.py @@ -1,7 +1,2 @@ from .special import * from .slugify import * - - -__author__ = 'Val Neekman @ Neekware Inc. [@vneekman]' -__description__ = 'A Python slugify application that also handles Unicode' -__version__ = '5.0.2' diff --git a/slugify/__main__.py b/slugify/__main__.py index f815206..ffdfd5f 100644 --- a/slugify/__main__.py +++ b/slugify/__main__.py @@ -77,7 +77,7 @@ def slugify_params(args): ) -def main(argv=None): # pragma: no cover +def main(argv=None): # pragma: no cover """ Run this program """ if argv is None: argv = sys.argv @@ -89,5 +89,5 @@ def main(argv=None): # pragma: no cover sys.exit(-1) -if __name__ == '__main__': # pragma: no cover +if __name__ == '__main__': # pragma: no cover main() diff --git a/slugify/__version__.py b/slugify/__version__.py new file mode 100644 index 0000000..72729f9 --- /dev/null +++ b/slugify/__version__.py @@ -0,0 +1,3 @@ +__author__ = 'Val Neekman @ Neekware Inc. [@vneekman]' +__description__ = 'A Python slugify application that also handles Unicode' +__version__ = '6.0.0' diff --git a/slugify/slugify.py b/slugify/slugify.py index bb3aa95..f38df10 100644 --- a/slugify/slugify.py +++ b/slugify/slugify.py @@ -1,17 +1,7 @@ import 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 +from html.entities import name2codepoint try: import text_unidecode as unidecode @@ -69,7 +59,7 @@ def smart_truncate(string, max_length=0, word_boundary=False, separator=' ', sav else: if save_order: break - if not truncated: # pragma: no cover + if not truncated: # pragma: no cover truncated = string[:max_length] return truncated.strip(separator) @@ -100,8 +90,8 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w text = text.replace(old, new) # ensure text is unicode - if not isinstance(text, _unicode_type): - text = _unicode(text, 'utf-8', 'ignore') + if not isinstance(text, str): + text = str(text, 'utf-8', 'ignore') # replace quotes with dashes - pre-process text = QUOTE_PATTERN.sub(DEFAULT_SEPARATOR, text) @@ -110,24 +100,24 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w text = unidecode.unidecode(text) # ensure text is still in unicode - if not isinstance(text, _unicode_type): - text = _unicode(text, 'utf-8', 'ignore') + if not isinstance(text, str): + text = str(text, 'utf-8', 'ignore') # character entity reference if entities: - text = CHAR_ENTITY_PATTERN.sub(lambda m: unichr(name2codepoint[m.group(1)]), text) + text = CHAR_ENTITY_PATTERN.sub(lambda m: chr(name2codepoint[m.group(1)]), text) # decimal character reference if decimal: try: - text = DECIMAL_PATTERN.sub(lambda m: unichr(int(m.group(1))), text) + text = DECIMAL_PATTERN.sub(lambda m: chr(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) + text = HEX_PATTERN.sub(lambda m: chr(int(m.group(1), 16)), text) except Exception: pass diff --git a/slugify/special.py b/slugify/special.py index d3478d5..54eb85c 100644 --- a/slugify/special.py +++ b/slugify/special.py @@ -20,7 +20,7 @@ def add_uppercase_char(char_list): (u'я', u'ya'), # ia (u'х', u'h'), # kh (u'у', u'y'), # u - (u'щ', u'sch'), # shch + (u'щ', u'sch'), # sch (u'ю', u'u'), # iu / yu ] CYRILLIC = add_uppercase_char(_CYRILLIC) diff --git a/test.py b/test.py index ddf1bf4..1cd56dc 100644 --- a/test.py +++ b/test.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import io -import os import sys import unittest from contextlib import contextmanager From 614f1830dece4140f51e7797670306f718ee7050 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 16:13:33 -0500 Subject: [PATCH 07/33] actions --- .github/workflows/dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index fc6d0a5..56d7302 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -10,7 +10,7 @@ on: jobs: build: - name: Python ${{ matrix.python-version }} + name: Python ${{ matrix.python }} runs-on: ubuntu-latest strategy: matrix: From d5e3586ca156d9f92a5d8b8195dfc132218a70e1 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 16:17:39 -0500 Subject: [PATCH 08/33] fix coverage --- .github/workflows/dev.yml | 2 +- .travis.yml | 22 ---------------------- 2 files changed, 1 insertion(+), 23 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 56d7302..d0cb401 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -37,7 +37,7 @@ jobs: pycodestyle --ignore=E128,E261,E225,E501,W605 slugify test.py setup.py - name: Run test run: | - coverage run --source=python test.py + coverage run --source=slugify test.py - name: Coveralls run: coveralls --service=github env: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2f8b3ce..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: python -dist: xenial - -python: - - "3.6" - - "3.7" - - "3.8" - - "3.9" - - "pypy3" - -install: - - pip install pip -U - - pip install -e . - - pip install pycodestyle - - pip install coveralls - -before_script: - - "bash format.sh" - -script: coverage run --source=slugify test.py - -after_success: coveralls From 9ada774c34b8b9939d5a83d42a798d0e4f82c843 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 16:23:46 -0500 Subject: [PATCH 09/33] actions --- CHANGELOG.md | 4 ++++ README.md | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 777f6dc..ed1f80f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.0 + +- Enable github action + ## 5.0.2 - Enable twine publish diff --git a/README.md b/README.md index 2305794..5476fbe 100644 --- a/README.md +++ b/README.md @@ -188,8 +188,8 @@ X.Y.Z Version `MINOR` version -- when you add functionality in a backwards-compatible manner, and `PATCH` version -- when you make backwards-compatible bug fixes. -[status-image]: https://travis-ci.org/un33k/python-slugify.svg?branch=master -[status-link]: https://travis-ci.org/un33k/python-slugify +[status-image]: https://github.com/un33k/python-slugify/actions/workflows/ci.yml/badge.svg +[status-link]: https://github.com/un33k/python-slugify/actions/workflows/ci.yml [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 From 7534199cf4b214309619e7486e1de1057bff74a3 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 16:30:41 -0500 Subject: [PATCH 10/33] version --- setup.py | 121 +++++++++++++++++++++++------------------ slugify/__version__.py | 7 ++- 2 files changed, 73 insertions(+), 55 deletions(-) diff --git a/setup.py b/setup.py index 8adc159..d9bd6fe 100755 --- a/setup.py +++ b/setup.py @@ -1,71 +1,84 @@ #!/usr/bin/env python - -# -*- coding: utf-8 -*- -from setuptools import setup, find_packages -import re +# Learn more: https://github.com/un33k/setup.py import os import sys -import codecs -name = 'python-slugify' +from codecs import open +from shutil import rmtree +from setuptools import setup + + 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' +python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +here = os.path.abspath(os.path.dirname(__file__)) + install_requires = ['text-unidecode>=1.3'] -extras_require = {'unidecode': ['Unidecode>=1.1.1']} +extras_requires = {'unidecode': ['Unidecode>=1.1.1']} +test_requires = [] -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 :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', -] +about = {} +with open(os.path.join(here, package, '__version__.py'), 'r', 'utf-8') as f: + exec(f.read(), about) +with open('README.md', 'r', 'utf-8') as f: + readme = f.read() -def get_version(package): - """ - Return package version as listed in `__version__` in `init.py`. - """ - init_py = codecs.open(os.path.join(package, '__version__.py'), encoding='utf-8').read() - return re.search("^__version__ = ['\"]([^'\"]+)['\"]", init_py, re.MULTILINE).group(1) +def status(s): + print('\033[1m{0}\033[0m'.format(s)) -if sys.argv[-1] == 'build': - os.system("python setup.py sdist bdist_wheel") +# 'setup.py publish' shortcut. if sys.argv[-1] == 'publish': - os.system("python setup.py build && twine upload dist/*") - 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() + try: + status('Removing previous builds…') + rmtree(os.path.join(here, 'dist')) + except OSError: + pass + + status('Building Source and Wheel (universal) distribution…') + os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable)) -EXCLUDE_FROM_PACKAGES = [] + status('Uploading the package to PyPI via Twine…') + os.system('twine upload dist/*') + + status('Pushing git tags…') + os.system('git tag v{0}'.format(about['__version__'])) + os.system('git push --tags') + sys.exit() 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), + name=about['__title__'], + version=about['__version__'], + description=about['__description__'], + long_description=readme, + long_description_content_type='text/markdown', + author=about['__author__'], + author_email=about['__author_email__'], + url=about['__url__'], + license=about['__license__'], + packages=[package], + package_data={'': ['LICENSE']}, + package_dir={'slugify': 'slugify'}, + include_package_data=True, + python_requires=python_requires, install_requires=install_requires, - extras_require=extras_require, - python_requires='>=3.6', - classifiers=classifiers, - entry_points={'console_scripts': ['slugify=slugify.__main__:main']}, -) + tests_require=test_requires, + extras_require=extras_requires, + zip_safe=False, + cmdclass={}, + project_urls={}, + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + ] +) \ No newline at end of file diff --git a/slugify/__version__.py b/slugify/__version__.py index 72729f9..4e5471b 100644 --- a/slugify/__version__.py +++ b/slugify/__version__.py @@ -1,3 +1,8 @@ -__author__ = 'Val Neekman @ Neekware Inc. [@vneekman]' +__title__ = 'python-slugify' +__author__ = 'Val Neekman' +__author_email__ = 'info@neekware.com' __description__ = 'A Python slugify application that also handles Unicode' +__url__ = 'https://github.com/un33k/python-slugify' +__license__ = 'MIT' +__copyright__ = 'Copyright 2022 Val Neekman @ Neekware Inc.' __version__ = '6.0.0' From 97915d932c40b3783dc0064c07f247bba1fb6929 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 16:32:21 -0500 Subject: [PATCH 11/33] manifest --- MANIFEST.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 0c78f18..373701c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,3 @@ -include CHANGELOG.md include LICENSE include README.md -include test.py +include CHANGELOG.md From 86161b412e4674fcd5bee4c2120d6fe2cd7fa18d Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 16:33:14 -0500 Subject: [PATCH 12/33] github action --- .github/workflows/ci.yml | 21 +++++++++++---------- .github/workflows/main.yml | 21 +++++++++++---------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34ea50c..d4998a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,7 @@ -name: (CI) +name: CI +# Run on push only for dev/sandbox +# Otherwise it may trigger concurrently `push & pull_request` on PRs. on: push: branches: @@ -7,19 +9,18 @@ on: jobs: build: - name: Setup ${{ matrix.python }} ${{ matrix.os }} - runs-on: ${{ matrix.os }} + name: Python ${{ matrix.python }} + runs-on: ubuntu-latest strategy: - fail-fast: false matrix: - os: [macos-latest, windows-latest, ubuntu-latest] python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + steps: - - name: setup-python ${{ matrix.python }} + - uses: actions/checkout@v2 + - name: setup python uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} - - name: Install dependencies run: | python -m pip install --upgrade pip @@ -28,14 +29,14 @@ jobs: - name: Run flake8 run: | pip install flake8 --upgrade - flake8 --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + flake8 --exclude=build --ignore=E501,F403,F401,E241,E225,E128 . - name: Run pycodestyle run: | pip install pycodestyle --upgrade - pycodestyle --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + pycodestyle --ignore=E128,E261,E225,E501,W605 slugify test.py setup.py - name: Run test run: | - coverage run --source=python test.py + coverage run --source=slugify test.py - name: Coveralls run: coveralls --service=github env: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7b7146b..f1e75b7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,7 @@ -name: (CI) +name: Main +# Run on push only for dev/sandbox +# Otherwise it may trigger concurrently `push & pull_request` on PRs. on: push: branches: @@ -7,19 +9,18 @@ on: jobs: build: - name: Setup ${{ matrix.python }} ${{ matrix.os }} - runs-on: ${{ matrix.os }} + name: Python ${{ matrix.python }} + runs-on: ubuntu-latest strategy: - fail-fast: false matrix: - os: [macos-latest, windows-latest, ubuntu-latest] python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + steps: - - name: setup-python ${{ matrix.python }} + - uses: actions/checkout@v2 + - name: setup python uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} - - name: Install dependencies run: | python -m pip install --upgrade pip @@ -28,14 +29,14 @@ jobs: - name: Run flake8 run: | pip install flake8 --upgrade - flake8 --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + flake8 --exclude=build --ignore=E501,F403,F401,E241,E225,E128 . - name: Run pycodestyle run: | pip install pycodestyle --upgrade - pycodestyle --exclude=migrations,tests --ignore=E501,E241,E225,E128 . + pycodestyle --ignore=E128,E261,E225,E501,W605 slugify test.py setup.py - name: Run test run: | - coverage run --source=python test.py + coverage run --source=slugify test.py - name: Coveralls run: coveralls --service=github env: From 8ea91da9f4c5e4ba12912ad2dff804abc4045e10 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 16:34:57 -0500 Subject: [PATCH 13/33] setup --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d9bd6fe..8fdb214 100755 --- a/setup.py +++ b/setup.py @@ -81,4 +81,4 @@ def status(s): 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', ] -) \ No newline at end of file +) From 7c4802eef3fdd2762d24b7f031f0d0e9305ad15d Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 16:47:31 -0500 Subject: [PATCH 14/33] coverall coverage --- setup.cfg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.cfg b/setup.cfg index 3c6e79c..26da821 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,7 @@ [bdist_wheel] universal=1 + +[coverage:run] +include = + slugify/slugify.py + slugify/special.py From c23bbc1dfbd50c64c342f9e6daf488b94639e4ba Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 16:51:48 -0500 Subject: [PATCH 15/33] coverall omit --- .github/workflows/dev.yml | 2 +- setup.cfg | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index d0cb401..729c26c 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -37,7 +37,7 @@ jobs: pycodestyle --ignore=E128,E261,E225,E501,W605 slugify test.py setup.py - name: Run test run: | - coverage run --source=slugify test.py + coverage run --include=slugify --omit=__version__.py --source=slugify test.py - name: Coveralls run: coveralls --service=github env: diff --git a/setup.cfg b/setup.cfg index 26da821..3c6e79c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,2 @@ [bdist_wheel] universal=1 - -[coverage:run] -include = - slugify/slugify.py - slugify/special.py From 57ee951f7c1c0fe48a75b198031a98046ab458fc Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 16:57:29 -0500 Subject: [PATCH 16/33] coverage run --- .github/workflows/dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 729c26c..d0cb401 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -37,7 +37,7 @@ jobs: pycodestyle --ignore=E128,E261,E225,E501,W605 slugify test.py setup.py - name: Run test run: | - coverage run --include=slugify --omit=__version__.py --source=slugify test.py + coverage run --source=slugify test.py - name: Coveralls run: coveralls --service=github env: From 28ac37cd980e80c12bfa950d8680ff9d4826678f Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 17:05:29 -0500 Subject: [PATCH 17/33] remove tox --- .python-version | 5 ----- test.py | 2 +- tox.ini | 18 ------------------ 3 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 .python-version delete mode 100644 tox.ini diff --git a/.python-version b/.python-version deleted file mode 100644 index a8733ab..0000000 --- a/.python-version +++ /dev/null @@ -1,5 +0,0 @@ -3.9.2 -3.8.8 -3.7.10 -3.6.13 -pypy3.7-7.3.3 diff --git a/test.py b/test.py index 1cd56dc..752c499 100644 --- a/test.py +++ b/test.py @@ -9,7 +9,7 @@ from slugify.__main__ import slugify_params, parse_args -class TestSlugification(unittest.TestCase): +class TestSlugify(unittest.TestCase): def test_extraneous_seperators(self): diff --git a/tox.ini b/tox.ini deleted file mode 100644 index a4bee82..0000000 --- a/tox.ini +++ /dev/null @@ -1,18 +0,0 @@ -[tox] -envlist = py{39,38,37,36},pypy3 - -[testenv] -deps= - -e . -commands = - python -m unittest test - -[testenv:format] -deps = pycodestyle -allowlist_externals = sh -commands = sh format.sh - -[testenv:coverage] -deps = coverage -commands = - coverage run --source=slugify test.py From 341aa39e801319c5c9de03a06c519466c4b9867e Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 17:09:20 -0500 Subject: [PATCH 18/33] clean readme --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 5476fbe..af10080 100644 --- a/README.md +++ b/README.md @@ -164,10 +164,6 @@ quick-brown-fox-jumps-over-lazy-dog # Running the tests -To run the tests against all environments: - - tox - To run the tests against the current environment: python test.py From bfb5170094e8065fcb113e32936ea356f7f420a2 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 17:11:23 -0500 Subject: [PATCH 19/33] clean up --- .python-version | 5 ----- README.md | 4 ---- test.py | 2 +- tox.ini | 18 ------------------ 4 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 .python-version delete mode 100644 tox.ini diff --git a/.python-version b/.python-version deleted file mode 100644 index a8733ab..0000000 --- a/.python-version +++ /dev/null @@ -1,5 +0,0 @@ -3.9.2 -3.8.8 -3.7.10 -3.6.13 -pypy3.7-7.3.3 diff --git a/README.md b/README.md index 5476fbe..af10080 100644 --- a/README.md +++ b/README.md @@ -164,10 +164,6 @@ quick-brown-fox-jumps-over-lazy-dog # Running the tests -To run the tests against all environments: - - tox - To run the tests against the current environment: python test.py diff --git a/test.py b/test.py index 1cd56dc..752c499 100644 --- a/test.py +++ b/test.py @@ -9,7 +9,7 @@ from slugify.__main__ import slugify_params, parse_args -class TestSlugification(unittest.TestCase): +class TestSlugify(unittest.TestCase): def test_extraneous_seperators(self): diff --git a/tox.ini b/tox.ini deleted file mode 100644 index a4bee82..0000000 --- a/tox.ini +++ /dev/null @@ -1,18 +0,0 @@ -[tox] -envlist = py{39,38,37,36},pypy3 - -[testenv] -deps= - -e . -commands = - python -m unittest test - -[testenv:format] -deps = pycodestyle -allowlist_externals = sh -commands = sh format.sh - -[testenv:coverage] -deps = coverage -commands = - coverage run --source=slugify test.py From e6e488d573ebbb6b3388529066652ebf41056410 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 17:12:23 -0500 Subject: [PATCH 20/33] change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed1f80f..8053b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 6.0.0 - Enable github action +- Remove tox, as we run the test on github action, the end users can refer to those test ## 5.0.2 From f4c176e043c8413db2914d0ff132a9f014a2f5ec Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 17:14:15 -0500 Subject: [PATCH 21/33] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed1f80f..8053b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 6.0.0 - Enable github action +- Remove tox, as we run the test on github action, the end users can refer to those test ## 5.0.2 From 86d76eead5430ec9b36b178dc0f3cb9b8ec20127 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 17:20:50 -0500 Subject: [PATCH 22/33] add github action, cleanup --- .github/workflows/ci.yml | 43 ++++++++++++++ .github/workflows/dev.yml | 44 ++++++++++++++ .github/workflows/main.yml | 43 ++++++++++++++ .python-version | 5 -- .travis.yml | 22 ------- .vscode/settings.json | 3 +- CHANGELOG.md | 5 ++ MANIFEST.in | 3 +- README.md | 8 +-- dev.requirements.txt | 3 +- setup.py | 118 +++++++++++++++++++++---------------- slugify/__init__.py | 5 -- slugify/__main__.py | 4 +- slugify/__version__.py | 8 +++ slugify/slugify.py | 31 ++++------ slugify/special.py | 2 +- test.py | 3 +- tox.ini | 18 ------ 18 files changed, 230 insertions(+), 138 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/dev.yml create mode 100644 .github/workflows/main.yml delete mode 100644 .python-version delete mode 100644 .travis.yml create mode 100644 slugify/__version__.py delete mode 100644 tox.ini diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d4998a5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +name: CI + +# Run on push only for dev/sandbox +# Otherwise it may trigger concurrently `push & pull_request` on PRs. +on: + push: + branches: + - ci + +jobs: + build: + name: Python ${{ matrix.python }} + runs-on: ubuntu-latest + strategy: + matrix: + python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + + steps: + - uses: actions/checkout@v2 + - name: setup python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + pip install coveralls --upgrade + - name: Run flake8 + run: | + pip install flake8 --upgrade + flake8 --exclude=build --ignore=E501,F403,F401,E241,E225,E128 . + - name: Run pycodestyle + run: | + pip install pycodestyle --upgrade + pycodestyle --ignore=E128,E261,E225,E501,W605 slugify test.py setup.py + - name: Run test + run: | + coverage run --source=slugify test.py + - name: Coveralls + run: coveralls --service=github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml new file mode 100644 index 0000000..d0cb401 --- /dev/null +++ b/.github/workflows/dev.yml @@ -0,0 +1,44 @@ +name: DEV + +# Run on push only for dev/sandbox +# Otherwise it may trigger concurrently `push & pull_request` on PRs. +on: + push: + branches: + - sandbox + - dev + +jobs: + build: + name: Python ${{ matrix.python }} + runs-on: ubuntu-latest + strategy: + matrix: + python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + + steps: + - uses: actions/checkout@v2 + - name: setup python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + pip install coveralls --upgrade + - name: Run flake8 + run: | + pip install flake8 --upgrade + flake8 --exclude=build --ignore=E501,F403,F401,E241,E225,E128 . + - name: Run pycodestyle + run: | + pip install pycodestyle --upgrade + pycodestyle --ignore=E128,E261,E225,E501,W605 slugify test.py setup.py + - name: Run test + run: | + coverage run --source=slugify test.py + - name: Coveralls + run: coveralls --service=github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..f1e75b7 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,43 @@ +name: Main + +# Run on push only for dev/sandbox +# Otherwise it may trigger concurrently `push & pull_request` on PRs. +on: + push: + branches: + - master + +jobs: + build: + name: Python ${{ matrix.python }} + runs-on: ubuntu-latest + strategy: + matrix: + python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + + steps: + - uses: actions/checkout@v2 + - name: setup python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + pip install coveralls --upgrade + - name: Run flake8 + run: | + pip install flake8 --upgrade + flake8 --exclude=build --ignore=E501,F403,F401,E241,E225,E128 . + - name: Run pycodestyle + run: | + pip install pycodestyle --upgrade + pycodestyle --ignore=E128,E261,E225,E501,W605 slugify test.py setup.py + - name: Run test + run: | + coverage run --source=slugify test.py + - name: Coveralls + run: coveralls --service=github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.python-version b/.python-version deleted file mode 100644 index a8733ab..0000000 --- a/.python-version +++ /dev/null @@ -1,5 +0,0 @@ -3.9.2 -3.8.8 -3.7.10 -3.6.13 -pypy3.7-7.3.3 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2f8b3ce..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: python -dist: xenial - -python: - - "3.6" - - "3.7" - - "3.8" - - "3.9" - - "pypy3" - -install: - - pip install pip -U - - pip install -e . - - pip install pycodestyle - - pip install coveralls - -before_script: - - "bash format.sh" - -script: coverage run --source=slugify test.py - -after_success: coveralls diff --git a/.vscode/settings.json b/.vscode/settings.json index 2ab09c1..ecfbb80 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "python.linting.pylintEnabled": false, "python.pythonPath": "/usr/bin/python3", -} \ No newline at end of file + "cSpell.words": ["Neekman", "shch", "xlate"] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 777f6dc..8053b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.0 + +- Enable github action +- Remove tox, as we run the test on github action, the end users can refer to those test + ## 5.0.2 - Enable twine publish diff --git a/MANIFEST.in b/MANIFEST.in index 0c78f18..373701c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,3 @@ -include CHANGELOG.md include LICENSE include README.md -include test.py +include CHANGELOG.md diff --git a/README.md b/README.md index 2305794..af10080 100644 --- a/README.md +++ b/README.md @@ -164,10 +164,6 @@ quick-brown-fox-jumps-over-lazy-dog # Running the tests -To run the tests against all environments: - - tox - To run the tests against the current environment: python test.py @@ -188,8 +184,8 @@ X.Y.Z Version `MINOR` version -- when you add functionality in a backwards-compatible manner, and `PATCH` version -- when you make backwards-compatible bug fixes. -[status-image]: https://travis-ci.org/un33k/python-slugify.svg?branch=master -[status-link]: https://travis-ci.org/un33k/python-slugify +[status-image]: https://github.com/un33k/python-slugify/actions/workflows/ci.yml/badge.svg +[status-link]: https://github.com/un33k/python-slugify/actions/workflows/ci.yml [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 diff --git a/dev.requirements.txt b/dev.requirements.txt index 337aa36..2b4e781 100644 --- a/dev.requirements.txt +++ b/dev.requirements.txt @@ -1,2 +1,3 @@ pycodestyle==2.7.0 -twine==3.4.1 \ No newline at end of file +twine==3.4.1 +flake8==4.0.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 51b267f..8fdb214 100755 --- a/setup.py +++ b/setup.py @@ -1,70 +1,84 @@ #!/usr/bin/env python - -# -*- coding: utf-8 -*- -from setuptools import setup, find_packages -import re +# Learn more: https://github.com/un33k/setup.py import os import sys -import codecs -name = 'python-slugify' +from codecs import open +from shutil import rmtree +from setuptools import setup + + 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' +python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +here = os.path.abspath(os.path.dirname(__file__)) + install_requires = ['text-unidecode>=1.3'] -extras_require = {'unidecode': ['Unidecode>=1.1.1']} +extras_requires = {'unidecode': ['Unidecode>=1.1.1']} +test_requires = [] -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 :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', -] +about = {} +with open(os.path.join(here, package, '__version__.py'), 'r', 'utf-8') as f: + exec(f.read(), about) +with open('README.md', 'r', 'utf-8') as f: + readme = f.read() -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) +def status(s): + print('\033[1m{0}\033[0m'.format(s)) -if sys.argv[-1] == 'build': - os.system("python setup.py sdist bdist_wheel") +# 'setup.py publish' shortcut. if sys.argv[-1] == 'publish': - os.system("python setup.py build && twine upload dist/*") - 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() + try: + status('Removing previous builds…') + rmtree(os.path.join(here, 'dist')) + except OSError: + pass + + status('Building Source and Wheel (universal) distribution…') + os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable)) -EXCLUDE_FROM_PACKAGES = [] + status('Uploading the package to PyPI via Twine…') + os.system('twine upload dist/*') + + status('Pushing git tags…') + os.system('git tag v{0}'.format(about['__version__'])) + os.system('git push --tags') + sys.exit() 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), + name=about['__title__'], + version=about['__version__'], + description=about['__description__'], + long_description=readme, + long_description_content_type='text/markdown', + author=about['__author__'], + author_email=about['__author_email__'], + url=about['__url__'], + license=about['__license__'], + packages=[package], + package_data={'': ['LICENSE']}, + package_dir={'slugify': 'slugify'}, + include_package_data=True, + python_requires=python_requires, install_requires=install_requires, - extras_require=extras_require, - python_requires='>=3.6', - classifiers=classifiers, - entry_points={'console_scripts': ['slugify=slugify.__main__:main']}, + tests_require=test_requires, + extras_require=extras_requires, + zip_safe=False, + cmdclass={}, + project_urls={}, + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + ] ) diff --git a/slugify/__init__.py b/slugify/__init__.py index 6c59f4e..ac21492 100644 --- a/slugify/__init__.py +++ b/slugify/__init__.py @@ -1,7 +1,2 @@ from .special import * from .slugify import * - - -__author__ = 'Val Neekman @ Neekware Inc. [@vneekman]' -__description__ = 'A Python slugify application that also handles Unicode' -__version__ = '5.0.2' diff --git a/slugify/__main__.py b/slugify/__main__.py index f815206..ffdfd5f 100644 --- a/slugify/__main__.py +++ b/slugify/__main__.py @@ -77,7 +77,7 @@ def slugify_params(args): ) -def main(argv=None): # pragma: no cover +def main(argv=None): # pragma: no cover """ Run this program """ if argv is None: argv = sys.argv @@ -89,5 +89,5 @@ def main(argv=None): # pragma: no cover sys.exit(-1) -if __name__ == '__main__': # pragma: no cover +if __name__ == '__main__': # pragma: no cover main() diff --git a/slugify/__version__.py b/slugify/__version__.py new file mode 100644 index 0000000..4e5471b --- /dev/null +++ b/slugify/__version__.py @@ -0,0 +1,8 @@ +__title__ = 'python-slugify' +__author__ = 'Val Neekman' +__author_email__ = 'info@neekware.com' +__description__ = 'A Python slugify application that also handles Unicode' +__url__ = 'https://github.com/un33k/python-slugify' +__license__ = 'MIT' +__copyright__ = 'Copyright 2022 Val Neekman @ Neekware Inc.' +__version__ = '6.0.0' diff --git a/slugify/slugify.py b/slugify/slugify.py index 6d4a67e..f38df10 100644 --- a/slugify/slugify.py +++ b/slugify/slugify.py @@ -1,18 +1,7 @@ import re import unicodedata -import typing -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 +from html.entities import name2codepoint try: import text_unidecode as unidecode @@ -70,14 +59,14 @@ def smart_truncate(string, max_length=0, word_boundary=False, separator=' ', sav else: if save_order: break - if not truncated: # pragma: no cover + 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: typing.Iterable[typing.Iterable[str]] = ()): + replacements=()): """ Make a slug from the given text. :param text (str): initial text @@ -101,8 +90,8 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w text = text.replace(old, new) # ensure text is unicode - if not isinstance(text, _unicode_type): - text = _unicode(text, 'utf-8', 'ignore') + if not isinstance(text, str): + text = str(text, 'utf-8', 'ignore') # replace quotes with dashes - pre-process text = QUOTE_PATTERN.sub(DEFAULT_SEPARATOR, text) @@ -111,24 +100,24 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w text = unidecode.unidecode(text) # ensure text is still in unicode - if not isinstance(text, _unicode_type): - text = _unicode(text, 'utf-8', 'ignore') + if not isinstance(text, str): + text = str(text, 'utf-8', 'ignore') # character entity reference if entities: - text = CHAR_ENTITY_PATTERN.sub(lambda m: unichr(name2codepoint[m.group(1)]), text) + text = CHAR_ENTITY_PATTERN.sub(lambda m: chr(name2codepoint[m.group(1)]), text) # decimal character reference if decimal: try: - text = DECIMAL_PATTERN.sub(lambda m: unichr(int(m.group(1))), text) + text = DECIMAL_PATTERN.sub(lambda m: chr(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) + text = HEX_PATTERN.sub(lambda m: chr(int(m.group(1), 16)), text) except Exception: pass diff --git a/slugify/special.py b/slugify/special.py index d3478d5..54eb85c 100644 --- a/slugify/special.py +++ b/slugify/special.py @@ -20,7 +20,7 @@ def add_uppercase_char(char_list): (u'я', u'ya'), # ia (u'х', u'h'), # kh (u'у', u'y'), # u - (u'щ', u'sch'), # shch + (u'щ', u'sch'), # sch (u'ю', u'u'), # iu / yu ] CYRILLIC = add_uppercase_char(_CYRILLIC) diff --git a/test.py b/test.py index ddf1bf4..752c499 100644 --- a/test.py +++ b/test.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import io -import os import sys import unittest from contextlib import contextmanager @@ -10,7 +9,7 @@ from slugify.__main__ import slugify_params, parse_args -class TestSlugification(unittest.TestCase): +class TestSlugify(unittest.TestCase): def test_extraneous_seperators(self): diff --git a/tox.ini b/tox.ini deleted file mode 100644 index a4bee82..0000000 --- a/tox.ini +++ /dev/null @@ -1,18 +0,0 @@ -[tox] -envlist = py{39,38,37,36},pypy3 - -[testenv] -deps= - -e . -commands = - python -m unittest test - -[testenv:format] -deps = pycodestyle -allowlist_externals = sh -commands = sh format.sh - -[testenv:coverage] -deps = coverage -commands = - coverage run --source=slugify test.py From b2aaccd0b0e8ac4780ff29221d7d0fe915aa3f0d Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 17:26:48 -0500 Subject: [PATCH 23/33] add staging to ci --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4998a5..4148354 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: push: branches: - ci + - staging jobs: build: From dce218991ddf16d916501fb6f72980f9ca0c892a Mon Sep 17 00:00:00 2001 From: Reza Moradi Date: Wed, 16 Feb 2022 23:36:00 +0100 Subject: [PATCH 24/33] Fix misleading pattern name and documentation (#109) * Add better typing for slugify.slugify Currently, mypy understands the type as `Iterable[str]`, which doesn't match what should actually be passed in, which is `Iterable[Iterable[str]]` or, ideally, `Iterable[Tuple[str, str]]` * whitespace around = * fix misleading pattern name and documentation * fix README.md and cli doc as well Co-authored-by: Fahrzin Hemmati Co-authored-by: Val Neekman (AvidCoder) --- README.md | 2 +- slugify/__main__.py | 2 +- slugify/slugify.py | 15 ++++++--------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index af10080..11e20da 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ def slugify( :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 regex_pattern (str): regex pattern for disallowed 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 diff --git a/slugify/__main__.py b/slugify/__main__.py index ffdfd5f..5a888fe 100644 --- a/slugify/__main__.py +++ b/slugify/__main__.py @@ -31,7 +31,7 @@ def parse_args(argv): parser.add_argument("--stopwords", nargs='+', help="Words to discount") parser.add_argument("--regex-pattern", - help="Python regex pattern for allowed characters") + help="Python regex pattern for disallowed characters") parser.add_argument("--no-lowercase", action='store_false', dest='lowercase', default=True, help="Activate case sensitivity") parser.add_argument("--replacements", nargs='+', diff --git a/slugify/slugify.py b/slugify/slugify.py index f38df10..190ea92 100644 --- a/slugify/slugify.py +++ b/slugify/slugify.py @@ -1,6 +1,7 @@ import re -import unicodedata import sys +import typing +import unicodedata from html.entities import name2codepoint try: @@ -15,8 +16,7 @@ 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]+') +DISALLOWED_CHARS_PATTERN = re.compile(r'[^-a-zA-Z0-9]+') DUPLICATE_DASH_PATTERN = re.compile(r'-{2,}') NUMBERS_PATTERN = re.compile(r'(?<=\d),(?=\d)') DEFAULT_SEPARATOR = '-' @@ -66,7 +66,7 @@ def smart_truncate(string, max_length=0, word_boundary=False, separator=' ', sav 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=()): + replacements: typing.Iterable[typing.Iterable[str]] = ()): """ Make a slug from the given text. :param text (str): initial text @@ -78,7 +78,7 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w :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 regex_pattern (str): regex pattern for disallowed 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): @@ -137,10 +137,7 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w 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 + pattern = regex_pattern or DISALLOWED_CHARS_PATTERN text = re.sub(pattern, DEFAULT_SEPARATOR, text) # remove redundant From d8c9d8a1220743f28f98de60a3eed9cbae30c624 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 17:41:14 -0500 Subject: [PATCH 25/33] regex_patter to disallow --- CHANGELOG.md | 5 +++++ slugify/__version__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8053b87..95ad243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.1 + +- Rework regex_pattern to mean the opposite (disallowed chars instead of allowed) +- Thanks to @yyyyyyyan for the initial PR followed by the final PR by @mrezzamoradi + ## 6.0.0 - Enable github action diff --git a/slugify/__version__.py b/slugify/__version__.py index 4e5471b..1eedf44 100644 --- a/slugify/__version__.py +++ b/slugify/__version__.py @@ -5,4 +5,4 @@ __url__ = 'https://github.com/un33k/python-slugify' __license__ = 'MIT' __copyright__ = 'Copyright 2022 Val Neekman @ Neekware Inc.' -__version__ = '6.0.0' +__version__ = '6.0.1' From c096bcdd76b0ef216716eccef5b19839266e1372 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 16 Feb 2022 17:46:24 -0500 Subject: [PATCH 26/33] regex: allow=>disallow --- .github/workflows/ci.yml | 1 + CHANGELOG.md | 5 +++++ README.md | 2 +- slugify/__main__.py | 2 +- slugify/__version__.py | 2 +- slugify/slugify.py | 15 ++++++--------- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4998a5..4148354 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: push: branches: - ci + - staging jobs: build: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8053b87..95ad243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.1 + +- Rework regex_pattern to mean the opposite (disallowed chars instead of allowed) +- Thanks to @yyyyyyyan for the initial PR followed by the final PR by @mrezzamoradi + ## 6.0.0 - Enable github action diff --git a/README.md b/README.md index af10080..11e20da 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ def slugify( :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 regex_pattern (str): regex pattern for disallowed 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 diff --git a/slugify/__main__.py b/slugify/__main__.py index ffdfd5f..5a888fe 100644 --- a/slugify/__main__.py +++ b/slugify/__main__.py @@ -31,7 +31,7 @@ def parse_args(argv): parser.add_argument("--stopwords", nargs='+', help="Words to discount") parser.add_argument("--regex-pattern", - help="Python regex pattern for allowed characters") + help="Python regex pattern for disallowed characters") parser.add_argument("--no-lowercase", action='store_false', dest='lowercase', default=True, help="Activate case sensitivity") parser.add_argument("--replacements", nargs='+', diff --git a/slugify/__version__.py b/slugify/__version__.py index 4e5471b..1eedf44 100644 --- a/slugify/__version__.py +++ b/slugify/__version__.py @@ -5,4 +5,4 @@ __url__ = 'https://github.com/un33k/python-slugify' __license__ = 'MIT' __copyright__ = 'Copyright 2022 Val Neekman @ Neekware Inc.' -__version__ = '6.0.0' +__version__ = '6.0.1' diff --git a/slugify/slugify.py b/slugify/slugify.py index f38df10..190ea92 100644 --- a/slugify/slugify.py +++ b/slugify/slugify.py @@ -1,6 +1,7 @@ import re -import unicodedata import sys +import typing +import unicodedata from html.entities import name2codepoint try: @@ -15,8 +16,7 @@ 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]+') +DISALLOWED_CHARS_PATTERN = re.compile(r'[^-a-zA-Z0-9]+') DUPLICATE_DASH_PATTERN = re.compile(r'-{2,}') NUMBERS_PATTERN = re.compile(r'(?<=\d),(?=\d)') DEFAULT_SEPARATOR = '-' @@ -66,7 +66,7 @@ def smart_truncate(string, max_length=0, word_boundary=False, separator=' ', sav 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=()): + replacements: typing.Iterable[typing.Iterable[str]] = ()): """ Make a slug from the given text. :param text (str): initial text @@ -78,7 +78,7 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w :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 regex_pattern (str): regex pattern for disallowed 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): @@ -137,10 +137,7 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w 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 + pattern = regex_pattern or DISALLOWED_CHARS_PATTERN text = re.sub(pattern, DEFAULT_SEPARATOR, text) # remove redundant From d968ca7419e6f4e40685888c56d03bea50fd39d7 Mon Sep 17 00:00:00 2001 From: Reza Moradi Date: Tue, 22 Feb 2022 20:05:08 +0100 Subject: [PATCH 27/33] allow unicode (#111) * initial commit to allow unicode * update version and changelog * add the flag to the CLI * update README.md --- CHANGELOG.md | 4 + README.md | 16 ++- slugify/__main__.py | 5 +- slugify/__version__.py | 2 +- slugify/slugify.py | 20 ++- test.py | 288 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 328 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95ad243..49f88dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.1.0 + +- Add `allow_unicode` flag to allow unicode characters in the slug + ## 6.0.1 - Rework regex_pattern to mean the opposite (disallowed chars instead of allowed) diff --git a/README.md b/README.md index 11e20da..f93afee 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,8 @@ def slugify( stopwords=(), regex_pattern=None, lowercase=True, - replacements=() + replacements=(), + allow_unicode=False ): """ Make a slug from the given text. @@ -58,6 +59,7 @@ def slugify( :param regex_pattern (str): regex pattern for disallowed characters :param lowercase (bool): activate case sensitivity by setting it to False :param replacements (iterable): list of replacement rules e.g. [['|', 'or'], ['%', 'percent']] + :param allow_unicode (bool): allow unicode characters :return (str): slugify text """ ``` @@ -75,6 +77,10 @@ txt = '影師嗎' r = slugify(txt) self.assertEqual(r, "ying-shi-ma") +txt = '影師嗎' +r = slugify(txt, allow_unicode=True) +self.assertEqual(r, "影師嗎") + txt = 'C\'est déjà l\'été.' r = slugify(txt) self.assertEqual(r, "c-est-deja-l-ete") @@ -133,6 +139,14 @@ txt = 'ÜBER Über German Umlaut' r = slugify(txt, replacements=[['Ü', 'UE'], ['ü', 'ue']]) self.assertEqual(r, "ueber-ueber-german-umlaut") +txt = 'i love 🦄' +r = slugify(txt, allow_unicode=True) +self.assertEqual(r, "i-love") + +txt = 'i love 🦄' +r = slugify(txt, allow_unicode=True, regex_pattern=r'[^🦄]+') +self.assertEqual(r, "🦄") + ``` For more examples, have a look at the [test.py](test.py) file. diff --git a/slugify/__main__.py b/slugify/__main__.py index 5a888fe..7dd6b01 100644 --- a/slugify/__main__.py +++ b/slugify/__main__.py @@ -36,6 +36,8 @@ def parse_args(argv): help="Activate case sensitivity") parser.add_argument("--replacements", nargs='+', help="""Additional replacement rules e.g. "|->or", "%%->percent".""") + parser.add_argument("--allow-unicode", action='store_true', default=False, + help="Allow unicode characters") args = parser.parse_args(argv[1:]) @@ -73,7 +75,8 @@ def slugify_params(args): separator=args.separator, stopwords=args.stopwords, lowercase=args.lowercase, - replacements=args.replacements + replacements=args.replacements, + allow_unicode=args.allow_unicode ) diff --git a/slugify/__version__.py b/slugify/__version__.py index 1eedf44..e14e887 100644 --- a/slugify/__version__.py +++ b/slugify/__version__.py @@ -5,4 +5,4 @@ __url__ = 'https://github.com/un33k/python-slugify' __license__ = 'MIT' __copyright__ = 'Copyright 2022 Val Neekman @ Neekware Inc.' -__version__ = '6.0.1' +__version__ = '6.1.0' diff --git a/slugify/slugify.py b/slugify/slugify.py index 190ea92..ae6c9b6 100644 --- a/slugify/slugify.py +++ b/slugify/slugify.py @@ -17,6 +17,7 @@ HEX_PATTERN = re.compile(r'&#x([\da-fA-F]+);') QUOTE_PATTERN = re.compile(r'[\']+') DISALLOWED_CHARS_PATTERN = re.compile(r'[^-a-zA-Z0-9]+') +DISALLOWED_UNICODE_CHARS_PATTERN = re.compile(r'[\W_]+') DUPLICATE_DASH_PATTERN = re.compile(r'-{2,}') NUMBERS_PATTERN = re.compile(r'(?<=\d),(?=\d)') DEFAULT_SEPARATOR = '-' @@ -66,7 +67,8 @@ def smart_truncate(string, max_length=0, word_boundary=False, separator=' ', sav 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: typing.Iterable[typing.Iterable[str]] = ()): + replacements: typing.Iterable[typing.Iterable[str]] = (), + allow_unicode=False): """ Make a slug from the given text. :param text (str): initial text @@ -81,6 +83,7 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w :param regex_pattern (str): regex pattern for disallowed characters :param lowercase (bool): activate case sensitivity by setting it to False :param replacements (iterable): list of replacement rules e.g. [['|', 'or'], ['%', 'percent']] + :param allow_unicode (bool): allow unicode characters :return (str): """ @@ -97,7 +100,8 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w text = QUOTE_PATTERN.sub(DEFAULT_SEPARATOR, text) # decode unicode - text = unidecode.unidecode(text) + if not allow_unicode: + text = unidecode.unidecode(text) # ensure text is still in unicode if not isinstance(text, str): @@ -122,7 +126,11 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w pass # translate - text = unicodedata.normalize('NFKD', text) + if allow_unicode: + text = unicodedata.normalize('NFKC', text) + else: + text = unicodedata.normalize('NFKD', text) + if sys.version_info < (3,): text = text.encode('ascii', 'ignore') @@ -137,7 +145,11 @@ def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, w text = NUMBERS_PATTERN.sub('', text) # replace all other unwanted characters - pattern = regex_pattern or DISALLOWED_CHARS_PATTERN + if allow_unicode: + pattern = regex_pattern or DISALLOWED_UNICODE_CHARS_PATTERN + else: + pattern = regex_pattern or DISALLOWED_CHARS_PATTERN + text = re.sub(pattern, DEFAULT_SEPARATOR, text) # remove redundant diff --git a/test.py b/test.py index 752c499..931f38f 100644 --- a/test.py +++ b/test.py @@ -233,6 +233,294 @@ def test_replacements_german_umlaut_custom(self): self.assertEqual(r, "ueber-ueber-german-umlaut") +class TestSlugifyUnicode(unittest.TestCase): + + def test_extraneous_seperators(self): + + txt = "This is a test ---" + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, "this-is-a-test") + + txt = "___This is a test ---" + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, "this-is-a-test") + + txt = "___This is a test___" + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, "this-is-a-test") + + def test_non_word_characters(self): + txt = "This -- is a ## test ---" + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, "this-is-a-test") + + def test_phonetic_conversion_of_eastern_scripts(self): + txt = '影師嗎' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, txt) + + def test_accented_text(self): + txt = 'C\'est déjà l\'été.' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, "c-est-déjà-l-été") + + txt = 'Nín hǎo. Wǒ shì zhōng guó rén' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, "nín-hǎo-wǒ-shì-zhōng-guó-rén") + + def test_accented_text_with_non_word_characters(self): + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, "jaja-lol-méméméoo-a") + + def test_cyrillic_text(self): + txt = 'Компьютер' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, "компьютер") + + def test_max_length(self): + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, allow_unicode=True, max_length=9) + self.assertEqual(r, "jaja-lol") + + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, allow_unicode=True, max_length=15) + self.assertEqual(r, "jaja-lol-mémémé") + + def test_max_length_cutoff_not_required(self): + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, allow_unicode=True, max_length=50) + self.assertEqual(r, "jaja-lol-méméméoo-a") + + def test_word_boundary(self): + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, allow_unicode=True, max_length=15, word_boundary=True) + self.assertEqual(r, "jaja-lol-a") + + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, allow_unicode=True, max_length=17, word_boundary=True) + self.assertEqual(r, "jaja-lol-méméméoo") + + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, allow_unicode=True, max_length=18, word_boundary=True) + self.assertEqual(r, "jaja-lol-méméméoo") + + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, allow_unicode=True, max_length=19, word_boundary=True) + self.assertEqual(r, "jaja-lol-méméméoo-a") + + def test_custom_separator(self): + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, allow_unicode=True, max_length=20, word_boundary=True, separator=".") + self.assertEqual(r, "jaja.lol.méméméoo.a") + + def test_multi_character_separator(self): + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, allow_unicode=True, max_length=20, word_boundary=True, separator="ZZZZZZ") + self.assertEqual(r, "jajaZZZZZZlolZZZZZZméméméooZZZZZZa") + + def test_save_order(self): + txt = 'one two three four five' + r = slugify(txt, allow_unicode=True, max_length=13, word_boundary=True, save_order=True) + self.assertEqual(r, "one-two-three") + + txt = 'one two three four five' + r = slugify(txt, allow_unicode=True, max_length=13, word_boundary=True, save_order=False) + self.assertEqual(r, "one-two-three") + + txt = 'one two three four five' + r = slugify(txt, allow_unicode=True, max_length=12, word_boundary=True, save_order=False) + self.assertEqual(r, "one-two-four") + + txt = 'one two three four five' + r = slugify(txt, allow_unicode=True, max_length=12, word_boundary=True, save_order=True) + self.assertEqual(r, "one-two") + + def test_save_order_rtl(self): + """For right-to-left unicode languages""" + txt = 'دو سه چهار پنج' + r = slugify(txt, allow_unicode=True, max_length=10, word_boundary=True, save_order=True) + self.assertEqual(r, "دو-سه-چهار") + + txt = 'دو سه چهار پنج' + r = slugify(txt, allow_unicode=True, max_length=10, word_boundary=True, save_order=False) + self.assertEqual(r, "دو-سه-چهار") + + txt = 'دو سه چهار پنج' + r = slugify(txt, allow_unicode=True, max_length=9, word_boundary=True, save_order=False) + self.assertEqual(r, "دو-سه-پنج") + + txt = 'دو سه چهار پنج' + r = slugify(txt, allow_unicode=True, max_length=9, word_boundary=True, save_order=True) + self.assertEqual(r, "دو-سه") + + def test_stopword_removal(self): + txt = 'this has a stopword' + r = slugify(txt, allow_unicode=True, stopwords=['stopword']) + self.assertEqual(r, 'this-has-a') + + txt = 'this has a Öländ' + r = slugify(txt, allow_unicode=True, stopwords=['Öländ']) + self.assertEqual(r, 'this-has-a') + + def test_stopword_removal_casesensitive(self): + txt = 'thIs Has a stopword Stopword' + r = slugify(txt, allow_unicode=True, stopwords=['Stopword'], lowercase=False) + self.assertEqual(r, 'thIs-Has-a-stopword') + + txt = 'thIs Has a öländ Öländ' + r = slugify(txt, allow_unicode=True, stopwords=['Öländ'], lowercase=False) + self.assertEqual(r, 'thIs-Has-a-öländ') + + def test_multiple_stopword_occurances(self): + txt = 'the quick brown fox jumps over the lazy dog' + r = slugify(txt, allow_unicode=True, 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, allow_unicode=True, stopwords=['foo']) + self.assertEqual(r, 'a-b-c') + + txt = 'Foo A FOO B foo C' + r = slugify(txt, allow_unicode=True, 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, allow_unicode=True, 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, allow_unicode=True, 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, allow_unicode=True) + self.assertEqual(r, 'foo-bar') + + def test_html_entities_off(self): + txt = 'foo & bår' + r = slugify(txt, allow_unicode=True, entities=False) + self.assertEqual(r, 'foo-amp-bår') + + def test_html_decimal_on(self): + txt = 'Ž' + r = slugify(txt, allow_unicode=True, decimal=True) + self.assertEqual(r, 'ž') + + def test_html_decimal_off(self): + txt = 'Ž' + r = slugify(txt, allow_unicode=True, entities=False, decimal=False) + self.assertEqual(r, '381') + + def test_html_hexadecimal_on(self): + txt = 'Ž' + r = slugify(txt, allow_unicode=True, hexadecimal=True) + self.assertEqual(r, 'ž') + + def test_html_hexadecimal_off(self): + txt = 'Ž' + r = slugify(txt, allow_unicode=True, hexadecimal=False) + self.assertEqual(r, 'x17d') + + def test_starts_with_number(self): + txt = '10 amazing secrets' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, '10-amazing-secrets') + + def test_contains_numbers(self): + txt = 'buildings with 1000 windows' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, 'buildings-with-1000-windows') + + def test_ends_with_number(self): + txt = 'recipe number 3' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, 'recipe-number-3') + + def test_numbers_only(self): + txt = '404' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, '404') + + def test_numbers_and_symbols(self): + txt = '1,000 reasons you are #1' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, '1000-reasons-you-are-1') + + txt = '۱,۰۰۰ reasons you are #۱' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, '۱۰۰۰-reasons-you-are-۱') + + def test_regex_pattern_keep_underscore(self): + """allowing unicode should not overrule the passed regex_pattern""" + txt = "___This is a test___" + regex_pattern = r'[^-a-z0-9_]+' + r = slugify(txt, allow_unicode=True, 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, allow_unicode=True, separator='_', regex_pattern=regex_pattern) + self.assertNotEqual(r, "_this_is_a_test_") + + def test_replacements(self): + txt = '10 | 20 %' + r = slugify(txt, allow_unicode=True, replacements=[['|', 'or'], ['%', 'percent']]) + self.assertEqual(r, "10-or-20-percent") + + txt = 'I ♥ 🦄' + r = slugify(txt, allow_unicode=True, replacements=[['♥', 'amour'], ['🦄', 'licorne']]) + self.assertEqual(r, "i-amour-licorne") + + txt = 'I ♥ 🦄' + r = slugify(txt, allow_unicode=True, replacements=[['♥', 'სიყვარული'], ['🦄', 'licorne']]) + self.assertEqual(r, "i-სიყვარული-licorne") + + def test_replacements_german_umlaut_custom(self): + txt = 'ÜBER Über German Umlaut' + r = slugify(txt, allow_unicode=True, replacements=[['Ü', 'UE'], ['ü', 'ue']]) + self.assertEqual(r, "ueber-ueber-german-umlaut") + + def test_emojis(self): + """ + allowing unicode shouldn't allow emojis, even in replacements. + the only exception is when it is allowed by the regex_pattern. regex_pattern overrules all + """ + txt = 'i love 🦄' + r = slugify(txt, allow_unicode=True) + self.assertEqual(r, "i-love") + + txt = 'i love 🦄' + r = slugify(txt, allow_unicode=True, decimal=True) + self.assertEqual(r, "i-love") + + txt = 'i love 🦄' + r = slugify(txt, allow_unicode=True, hexadecimal=True) + self.assertEqual(r, "i-love") + + txt = 'i love 🦄' + r = slugify(txt, allow_unicode=True, entities=True) + self.assertEqual(r, "i-love") + + txt = 'i love you' + r = slugify(txt, allow_unicode=True, replacements=[['you', '🦄']]) + self.assertEqual(r, "i-love") + + txt = 'i love 🦄' + r = slugify(txt, allow_unicode=True, regex_pattern=r'[^🦄]+') + self.assertEqual(r, "🦄") + + class TestUtils(unittest.TestCase): def test_smart_truncate_no_max_length(self): From 0bf1b8761f695e5ae14d9c439a05b151b4a1093d Mon Sep 17 00:00:00 2001 From: Reza Moradi Date: Sat, 26 Feb 2022 19:55:55 +0100 Subject: [PATCH 28/33] remove type hinting (#113) --- CHANGELOG.md | 4 ++++ slugify/__version__.py | 2 +- slugify/slugify.py | 4 +--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49f88dd..12d0ff1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.1.1 + +- Remove type hinting (temporarily) + ## 6.1.0 - Add `allow_unicode` flag to allow unicode characters in the slug diff --git a/slugify/__version__.py b/slugify/__version__.py index e14e887..f971770 100644 --- a/slugify/__version__.py +++ b/slugify/__version__.py @@ -5,4 +5,4 @@ __url__ = 'https://github.com/un33k/python-slugify' __license__ = 'MIT' __copyright__ = 'Copyright 2022 Val Neekman @ Neekware Inc.' -__version__ = '6.1.0' +__version__ = '6.1.1' diff --git a/slugify/slugify.py b/slugify/slugify.py index ae6c9b6..b8c02ad 100644 --- a/slugify/slugify.py +++ b/slugify/slugify.py @@ -1,6 +1,5 @@ import re import sys -import typing import unicodedata from html.entities import name2codepoint @@ -67,8 +66,7 @@ def smart_truncate(string, max_length=0, word_boundary=False, separator=' ', sav 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: typing.Iterable[typing.Iterable[str]] = (), - allow_unicode=False): + replacements=(), allow_unicode=False): """ Make a slug from the given text. :param text (str): initial text From 3f1a0fe7c5775a72141163ccdd593272e512898c Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Wed, 27 Apr 2022 12:08:35 -0400 Subject: [PATCH 29/33] Add cmdLine option --- CHANGELOG.md | 4 ++++ dev.requirements.txt | 2 +- setup.py | 3 ++- slugify/__version__.py | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12d0ff1..2ba0bb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.1.2 + +- Reintroduce the cli options + ## 6.1.1 - Remove type hinting (temporarily) diff --git a/dev.requirements.txt b/dev.requirements.txt index 2b4e781..5f94d7b 100644 --- a/dev.requirements.txt +++ b/dev.requirements.txt @@ -1,3 +1,3 @@ -pycodestyle==2.7.0 +pycodestyle==2.8.0 twine==3.4.1 flake8==4.0.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 8fdb214..c3c4b3b 100755 --- a/setup.py +++ b/setup.py @@ -80,5 +80,6 @@ def status(s): 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', - ] + ], + entry_points={'console_scripts': ['slugify=slugify.__main__:main']}, ) diff --git a/slugify/__version__.py b/slugify/__version__.py index f971770..55abc97 100644 --- a/slugify/__version__.py +++ b/slugify/__version__.py @@ -5,4 +5,4 @@ __url__ = 'https://github.com/un33k/python-slugify' __license__ = 'MIT' __copyright__ = 'Copyright 2022 Val Neekman @ Neekware Inc.' -__version__ = '6.1.1' +__version__ = '6.1.2' From dbe74897764eb8338151e09cfc611e90cef12280 Mon Sep 17 00:00:00 2001 From: eNV25 Date: Tue, 27 Sep 2022 17:28:23 +0530 Subject: [PATCH 30/33] Update __init__.py to have __verstion__ variables. (#116) This allows you to use `from slugify import __version__ as slugifyVersion`, while `from slugify.__version__ import __version__ as slugifyVersion` continues to work. --- slugify/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/slugify/__init__.py b/slugify/__init__.py index ac21492..6d3279f 100644 --- a/slugify/__init__.py +++ b/slugify/__init__.py @@ -1,2 +1,10 @@ from .special import * from .slugify import * +from .__version__ import __title__ +from .__version__ import __author__ +from .__version__ import __author_email__ +from .__version__ import __description__ +from .__version__ import __url__ +from .__version__ import __license__ +from .__version__ import __copyright__ +from .__version__ import __version__ From a229e483b6f82f36600e73e7a2fb62dc2f5e7ed3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 18 Nov 2022 20:36:21 +0200 Subject: [PATCH 31/33] Add support for Python 3.11 (#121) * Add support for Python 3.11 * Drop support for EOL Python 3.6 --- .github/workflows/ci.yml | 6 +++--- .github/workflows/dev.yml | 6 +++--- .github/workflows/main.yml | 6 +++--- setup.cfg | 2 -- setup.py | 4 ++-- 5 files changed, 11 insertions(+), 13 deletions(-) delete mode 100644 setup.cfg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4148354..56e4097 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,12 +14,12 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + python: [3.7, 3.8, 3.9, "3.10", 3.11, pypy3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: setup python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - name: Install dependencies diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index d0cb401..73f2f07 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -14,12 +14,12 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + python: [3.7, 3.8, 3.9, "3.10", 3.11, pypy3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: setup python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - name: Install dependencies diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f1e75b7..a4ae9ad 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,12 +13,12 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + python: [3.7, 3.8, 3.9, "3.10", 3.11, pypy3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: setup python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - name: Install dependencies diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 3c6e79c..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -universal=1 diff --git a/setup.py b/setup.py index c3c4b3b..65c72f4 100755 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ package = 'slugify' -python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python_requires = ">=3.7" here = os.path.abspath(os.path.dirname(__file__)) install_requires = ['text-unidecode>=1.3'] @@ -75,11 +75,11 @@ def status(s): 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', ], entry_points={'console_scripts': ['slugify=slugify.__main__:main']}, ) From 247fe299c524f48adb94325ce79c84d87fbd0197 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Fri, 18 Nov 2022 14:03:48 -0500 Subject: [PATCH 32/33] drop py 3.6, add py 3.11, upversion --- CHANGELOG.md | 4 ++++ README.md | 1 + slugify/__version__.py | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ba0bb3..7e32d42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 7.0.0 + +- Drop python 3.6, add python 3.11 (@hugovk - thx) + ## 6.1.2 - Reintroduce the cli options diff --git a/README.md b/README.md index f93afee..2514fc5 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ However, there is an alternative decoding package called [Unidecode](https://git - Python `2.7` <-> python-slugify `< 5.0.0` - Python `3.6+` <-> python-slugify `>= 5.0.0` +- Python `3.7+` <-> python-slugify `>= 7.0.0` # How to install diff --git a/slugify/__version__.py b/slugify/__version__.py index 55abc97..184339a 100644 --- a/slugify/__version__.py +++ b/slugify/__version__.py @@ -5,4 +5,4 @@ __url__ = 'https://github.com/un33k/python-slugify' __license__ = 'MIT' __copyright__ = 'Copyright 2022 Val Neekman @ Neekware Inc.' -__version__ = '6.1.2' +__version__ = '7.0.0' From dc888f5a7a0c52c0e408a9bded308505cf962fc1 Mon Sep 17 00:00:00 2001 From: "Val Neekman (AvidCoder)" Date: Tue, 22 Nov 2022 13:05:35 -0500 Subject: [PATCH 33/33] SemVer Table --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2514fc5..04b72ab 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,11 @@ However, there is an alternative decoding package called [Unidecode](https://git ### Python Versions & `Official` Support -- Python `2.7` <-> python-slugify `< 5.0.0` -- Python `3.6+` <-> python-slugify `>= 5.0.0` -- Python `3.7+` <-> python-slugify `>= 7.0.0` +| python version | python-slugify version | +| -------------- | ---------------------- | +| `=2.7` | `< 5.0.0` | +| `<=3.6` | `>= 5.0.0 < 7.0.0` | +| `>=3.7` | `>= 7.0.0` | # How to install