diff options
author | Val Neekman <val@neekware.com> | 2012-10-14 18:49:13 -0700 |
---|---|---|
committer | Val Neekman <val@neekware.com> | 2012-10-14 18:49:13 -0700 |
commit | 64ffcbcedee3bead5e3a20cb1c545854b3bd651b (patch) | |
tree | 5b9fd44f1b7e55befc6df7321a092fdd212ebe61 | |
parent | be60050791262776db3c59ff6c4bf7da7c2a1b43 (diff) | |
download | python-slugify-64ffcbcedee3bead5e3a20cb1c545854b3bd651b.tar.gz |
Initial version
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | .travis.yml | 8 | ||||
-rw-r--r-- | README.md | 92 | ||||
-rwxr-xr-x | pep8.sh | 5 | ||||
-rw-r--r-- | requirements.txt | 2 | ||||
-rwxr-xr-x | setup.py | 83 | ||||
-rw-r--r-- | slugify/__init__.py | 67 | ||||
-rw-r--r-- | test.py | 29 |
8 files changed, 289 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..046cca1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.pyc +MANIFEST +dist/ +build/ +*.egg-info/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6990722 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: python +python: + - "2.6" + - "2.7" +install: + - pip install -q -r requirements.txt --use-mirrors +script: python test.py + @@ -1,2 +1,90 @@ -python-slugify -==============
\ No newline at end of file +Python Slugify +==================== + +**A Python slugify application that handles unicode** + +**Author:** Val Neekman [ info@neekware.com, @vneekman ] + +Overview +======== + +A Python slugify application that handles unicode. + +How to install +================== + + 1. easy_install python-slugify + 2. pip install python-slugify + 3. git clone http://github.com/un33k/python-slugify + a. cd python-slugify + b. run python setup.py + 4. wget https://github.com/un33k/python-slugify/zipball/master + a. unzip the downloaded file + b. cd into python-slugify-* directory + c. run python setup.py + +How to use +================= + from slugify import slugify + + s = "This is a test ---" + r = slugify(s) + self.assertEquals(r, "this-is-a-test") + + s = 'C\'est déjà l\'été.' + r = slugify(s) + self.assertEquals(r, "c-est-deja-lete") + + s = 'Nín hǎo. Wǒ shì zhōng guó rén' + r = slugify(s) + self.assertEquals(r, "nin-hao-wo-shi-zhong-guo-ren") + + s = '影師嗎' + r = slugify(s) + self.assertEquals(r, "ying-shi-ma") + + +Running the tests +================= + +To run the tests against the current environment: + + python test.py + +Changelog +========= + +0.0.1 +----- + +* Initial release + + +License +======= + +Copyright © Neekware Inc. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + @@ -0,0 +1,5 @@ +#!/bin/bash + +echo -e "\nRunning: (pep8 --show-source --show-pep8 --select=errors --testsuite=.)\n\n" +pep8 --show-source --show-pep8 --select=errors --testsuite=./ +echo -e "\n\n" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..000ccb7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +Unidecode>=0.04.9 + diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..e2df292 --- /dev/null +++ b/setup.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python + +# -*- coding: utf-8 -*- +from setuptools import setup +import re +import os +import sys + + +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 = 'BSD' +install_requires = ['Unidecode>=0.04.9'] +classifiers = [ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: POSIX', + 'Programming Language :: Python', +] + + +def get_version(package): + """ + Return package version as listed in `__version__` in `init.py`. + """ + init_py = open(os.path.join(package, '__init__.py')).read() + return re.search("^__version__ = ['\"]([^'\"]+)['\"]", init_py, re.MULTILINE).group(1) + + +def get_packages(package): + """ + Return root package and all sub-packages. + """ + return [dirpath + for dirpath, dirnames, filenames in os.walk(package) + if os.path.exists(os.path.join(dirpath, '__init__.py'))] + + +def get_package_data(package): + """ + Return all files under the root package, that are not in a + package themselves. + """ + walk = [(dirpath.replace(package + os.sep, '', 1), filenames) + for dirpath, dirnames, filenames in os.walk(package) + if not os.path.exists(os.path.join(dirpath, '__init__.py'))] + + filepaths = [] + for base, filenames in walk: + filepaths.extend([os.path.join(base, filename) + for filename in filenames]) + return {package: filepaths} + + +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'" % args + print " git push --tags" + sys.exit() + + +setup( + name=name, + version=get_version(package), + url=url, + license=license, + description=description, + author=author, + author_email=author_email, + packages=get_packages(package), + package_data=get_package_data(package), + install_requires=install_requires, + classifiers=classifiers +) + + diff --git a/slugify/__init__.py b/slugify/__init__.py new file mode 100644 index 0000000..94d0bc1 --- /dev/null +++ b/slugify/__init__.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- + +__version__ = '0.0.1' + +__all__ = ['slugify'] + +import re +import unicodedata +from htmlentitydefs import name2codepoint +from types import UnicodeType +from unidecode import unidecode + +# character entity reference +CHAR_ENTITY_REXP = re.compile('&(%s);' % '|'.join(name2codepoint)) + +# decimal character reference +DECIMAL_REXP = re.compile('&#(\d+);') + +# hexadecimal character reference +HEX_REXP = re.compile('&#x([\da-fA-F]+);') + +REPLACE1_REXP = re.compile(r'[\']+') +REPLACE2_REXP = re.compile(r'[^-a-z0-9]+') +REMOVE_REXP = re.compile('-{2,}') + +def slugify(text, entities=True, decimal=True, hexadecimal=True): + """ Make a slug from the given text """ + + # text to unicode + if type(text) != UnicodeType: + text = unicode(text, 'utf-8', 'ignore') + + # decode unicode ( 影師嗎 = Ying Shi Ma) + text = unidecode(text) + + # text back to unicode + text = unicode(text, 'utf-8', 'ignore') + + # character entity reference + if entities: + text = CHAR_ENTITY_REXP.sub(lambda m: unichr(name2codepoint[m.group(1)]), text) + + # decimal character reference + if decimal: + try: + text = DECIMAL_REXP.sub(lambda m: unichr(int(m.group(1))), text) + except: + pass + + # hexadecimal character reference + if hexadecimal: + try: + text = HEX_REXP.sub(lambda m: unichr(int(m.group(1), 16)), text) + except: + pass + + # translate + text = unicodedata.normalize('NFKD', text).encode('ascii', 'ignore') + + # replace unwanted characters + text = REPLACE1_REXP.sub('', text.lower()) # replace ' with nothing instead with - + text = REPLACE2_REXP.sub('-', text.lower()) + + # remove redundant - + text = REMOVE_REXP.sub('-', text).strip('-') + + return text @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +import unittest +from slugify import slugify + +class TestSequenceFunctions(unittest.TestCase): + + def test_space_dash(self): + txt = "This is a test ---" + r = slugify(txt) + self.assertEquals(r, "this-is-a-test") + + def test_special_chars(self): + txt = 'C\'est déjà l\'été.' + r = slugify(txt) + self.assertEquals(r, "cest-deja-lete") + + txt = 'Nín hǎo. Wǒ shì zhōng guó rén' + r = slugify(txt) + self.assertEquals(r, "nin-hao-wo-shi-zhong-guo-ren") + + txt = '影師嗎' + r = slugify(txt) + self.assertEquals(r, "ying-shi-ma") + +if __name__ == '__main__': + unittest.main() + + |