diff --git a/.gitignore b/.gitignore index 6cfc82d..046cca1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,5 @@ *.pyc -*.pyo -*~ -.installed.cfg -bin -develop-eggs -dist -downloads -eggs -parts -*.egg-info -.DS_Store - - +MANIFEST +dist/ +build/ +*.egg-info/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7d24604 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: python +python: + - "2.6" + - "2.7" +env: + - DJANGO_VERSION=1.3 + - DJANGO_VERSION=1.4 +install: + - pip install -q Django==$DJANGO_VERSION --use-mirrors + - pip install -q -r requirements.txt --use-mirrors +script: python manage.py test diff --git a/HISTORY b/HISTORY deleted file mode 100644 index 60f4548..0000000 --- a/HISTORY +++ /dev/null @@ -1,15 +0,0 @@ - -- August 6, 2012 (v 0.8) ------------------------- -1. ORM Optimization & Split of Unicode and Uniqueness into routines (credit: jedie @ github) - -- August 6, 2012 (v 0.7) ------------------------- -1. history file created to track the changes -2. singleton "'" handling feature broke the testunit (fix pulled in from re_compile branch) -3. re cleanup (pulled in from re_compile branch) -4. up the version -5. updated the credit section from the README as the referred snippet no longer exists -6. include full name in setup.py -7. up Django to 1.4.1 - diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 741cb7d..0000000 --- a/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright (c) 2012 Val Neekman (val@neekware.com) -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. 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. - - 3. Neither the name of this project nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -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 OWNER 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. - - - diff --git a/README b/README deleted file mode 100644 index 50485b8..0000000 --- a/README +++ /dev/null @@ -1,104 +0,0 @@ -django-uuslug -================ - -A Django slugify application that guarantees uniqueness and handles unicode. -UUSlug = (``U``nique + ``U``nicode Slug) - -Patches welcome: https://github.com/un33k/django-uuslug - -Usage -===== - -A. Install django-uuslug: - * _ Make sure you have python 2.6+ and can install from pypi - 1. easy_install django-uuslug - 2. pip install django-uuslug - 3. git clone http://github.com/un33k/django-uuslug - a. cd django-uuslug - b. run python setup.py - 4. wget https://github.com/un33k/django-uuslug/zipball/master - a. unzip the downloaded file - b. cd into django-uuslug-* directory - c. run python setup.py - -B. Test & contribute to django-uuslug: (for developers) - * _ git clone http://github.com/un33k/django-uuslug - a. cd into django-uuslug - b. run python bootstrap.py - c. run bin/buildout -vvvvv - d. run bin/test - - -Unicode Test Example -===================== -from uuslug 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") - - -Uniqueness Test Example -======================= -Override your object's save method with something like this (models.py) - -from django.db import models -from uuslug import uuslug - -class CoolSlug(models.Model): - name = models.CharField(max_length=100) - slug = models.CharField(max_length=200) - - def __unicode__(self): - return self.name - - def save(self, *args, **kwargs): - self.slug = uuslug(self.name, instance=self) - super(CoolSlug, self).save(*args, **kwargs) - - -Note: You can also specify the start number. -Example: - self.slug = uuslug(self.name, instance=self, start_no=2) - # the second slug should start with "-2" instead of "-1" - - -Test: -===== - -name = "john" -c = CoolSlug.objects.create(name=name) -c.save() -self.assertEquals(c.slug, name) # slug = "john" - -c1 = CoolSlug.objects.create(name=name) -c1.save() -self.assertEquals(c1.slug, name+"-1") # slug = "john-1" - -c1 = CoolSlug.objects.create(name=name) -c1.save() -self.assertEquals(c1.slug, name+"-2") # slug = "john-2" - -ToDo: -===== -clean up README -add more test and examples - -Credits: -========= -This is inspired by a snippet from http://www.djangosnippets.org/ -Improved to handle unique slugs and unicode chars and packaged for easy install. - - diff --git a/README.md b/README.md new file mode 100644 index 0000000..11b7f7f --- /dev/null +++ b/README.md @@ -0,0 +1,149 @@ +Django Unique-Unicode Slug Application +==================== + +**A Django slugify application that guarantees uniqueness and handles unicode** + +**Author:** Val Neekman, [ info@neekware.com, @vneekman] + +Overview +======== + +A Django slugify application that guarantees uniqueness and handles unicode. +UUSlug == (``U``nique + ``U``nicode) Slug + +How to install +================== + + 1. easy_install django-uuslug + 2. pip install django-uuslug + 3. git clone http://github.com/un33k/django-uuslug + a. cd django-uuslug + b. run python setup.py + 4. wget https://github.com/un33k/django-uuslug/zipball/master + a. unzip the downloaded file + b. cd into django-uuslug-* directory + c. run python setup.py + +How to use +================= + # Unicode Test + ====================== + from uuslug import slugify + + s = "This is a test ---" + r = slugify(s) + print r # => "this-is-a-test" + + s = 'C\'est déjà l\'été.' + r = slugify(s) + print r # => "c-est-deja-lete" + + s = 'Nín hǎo. Wǒ shì zhōng guó rén' + r = slugify(s) + print r # => "nin-hao-wo-shi-zhong-guo-ren" + + s = '影師嗎' + r = slugify(s) + print r # => "ying-shi-ma" + + txt = 'Компьютер' + r = slugify(txt) + print r # => "kompiuter" + + Uniqueness Test + ======================= + Override your object's save method with something like this (models.py) + + from django.db import models + from uuslug import uuslug + + class CoolSlug(models.Model): + name = models.CharField(max_length=100) + slug = models.CharField(max_length=200) + + def __unicode__(self): + return self.name + + def save(self, *args, **kwargs): + self.slug = uuslug(self.name, instance=self) + super(CoolSlug, self).save(*args, **kwargs) + + + Note: You can also specify the start number. + Example: + self.slug = uuslug(self.name, instance=self, start_no=2) + # the second slug should start with "-2" instead of "-1" + + name = "john" + c = CoolSlug.objects.create(name=name) + c.save() + print c.slug # => "john" + + c1 = CoolSlug.objects.create(name=name) + c1.save() + print c1.slug # => "john-1" + + c2 = CoolSlug.objects.create(name=name) + c2.save() + print c2.slug # => "john-2" + + +Running the tests +================= + +To run the tests against the current environment: + + python manage.py test + + +Changelog +========= + +0.9 +----- +* removed buildout dependency +* split unicode slugify into its own python module + +0.8 +----- +* ORM Optimization & Split of Unicode and Uniqueness into routines (credit: jedie @ github) + +0.1 - 0.7 +----- +* history file created to track the changes +* singleton "'" handling feature broke the testunit (fix pulled in from re_compile branch) +* re cleanup (pulled in from re_compile branch) +* up the version +* updated the credit section from the README as the referred snippet no longer exists +* include full name in setup.py +* 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. + + + diff --git a/bootstrap.py b/bootstrap.py deleted file mode 100644 index 7647cbb..0000000 --- a/bootstrap.py +++ /dev/null @@ -1,262 +0,0 @@ -############################################################################## -# -# Copyright (c) 2006 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Bootstrap a buildout-based project - -Simply run this script in a directory containing a buildout.cfg. -The script accepts buildout command-line options, so you can -use the -c option to specify an alternate configuration file. -""" - -import os, shutil, sys, tempfile, urllib, urllib2, subprocess -from optparse import OptionParser - -if sys.platform == 'win32': - def quote(c): - if ' ' in c: - return '"%s"' % c # work around spawn lamosity on windows - else: - return c -else: - quote = str - -# See zc.buildout.easy_install._has_broken_dash_S for motivation and comments. -stdout, stderr = subprocess.Popen( - [sys.executable, '-Sc', - 'try:\n' - ' import ConfigParser\n' - 'except ImportError:\n' - ' print 1\n' - 'else:\n' - ' print 0\n'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() -has_broken_dash_S = bool(int(stdout.strip())) - -# In order to be more robust in the face of system Pythons, we want to -# run without site-packages loaded. This is somewhat tricky, in -# particular because Python 2.6's distutils imports site, so starting -# with the -S flag is not sufficient. However, we'll start with that: -if not has_broken_dash_S and 'site' in sys.modules: - # We will restart with python -S. - args = sys.argv[:] - args[0:0] = [sys.executable, '-S'] - args = map(quote, args) - os.execv(sys.executable, args) -# Now we are running with -S. We'll get the clean sys.path, import site -# because distutils will do it later, and then reset the path and clean -# out any namespace packages from site-packages that might have been -# loaded by .pth files. -clean_path = sys.path[:] -import site # imported because of its side effects -sys.path[:] = clean_path -for k, v in sys.modules.items(): - if k in ('setuptools', 'pkg_resources') or ( - hasattr(v, '__path__') and - len(v.__path__) == 1 and - not os.path.exists(os.path.join(v.__path__[0], '__init__.py'))): - # This is a namespace package. Remove it. - sys.modules.pop(k) - -is_jython = sys.platform.startswith('java') - -setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py' -distribute_source = 'http://python-distribute.org/distribute_setup.py' - - -# parsing arguments -def normalize_to_url(option, opt_str, value, parser): - if value: - if '://' not in value: # It doesn't smell like a URL. - value = 'file://%s' % ( - urllib.pathname2url( - os.path.abspath(os.path.expanduser(value))),) - if opt_str == '--download-base' and not value.endswith('/'): - # Download base needs a trailing slash to make the world happy. - value += '/' - else: - value = None - name = opt_str[2:].replace('-', '_') - setattr(parser.values, name, value) - -usage = '''\ -[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options] - -Bootstraps a buildout-based project. - -Simply run this script in a directory containing a buildout.cfg, using the -Python that you want bin/buildout to use. - -Note that by using --setup-source and --download-base to point to -local resources, you can keep this script from going over the network. -''' - -parser = OptionParser(usage=usage) -parser.add_option("-v", "--version", dest="version", - help="use a specific zc.buildout version") -parser.add_option("-d", "--distribute", - action="store_true", dest="use_distribute", default=False, - help="Use Distribute rather than Setuptools.") -parser.add_option("--setup-source", action="callback", dest="setup_source", - callback=normalize_to_url, nargs=1, type="string", - help=("Specify a URL or file location for the setup file. " - "If you use Setuptools, this will default to " + - setuptools_source + "; if you use Distribute, this " - "will default to " + distribute_source + ".")) -parser.add_option("--download-base", action="callback", dest="download_base", - callback=normalize_to_url, nargs=1, type="string", - help=("Specify a URL or directory for downloading " - "zc.buildout and either Setuptools or Distribute. " - "Defaults to PyPI.")) -parser.add_option("--eggs", - help=("Specify a directory for storing eggs. Defaults to " - "a temporary directory that is deleted when the " - "bootstrap script completes.")) -parser.add_option("-t", "--accept-buildout-test-releases", - dest='accept_buildout_test_releases', - action="store_true", default=False, - help=("Normally, if you do not specify a --version, the " - "bootstrap script and buildout gets the newest " - "*final* versions of zc.buildout and its recipes and " - "extensions for you. If you use this flag, " - "bootstrap and buildout will get the newest releases " - "even if they are alphas or betas.")) -parser.add_option("-c", None, action="store", dest="config_file", - help=("Specify the path to the buildout configuration " - "file to be used.")) - -options, args = parser.parse_args() - -# if -c was provided, we push it back into args for buildout's main function -if options.config_file is not None: - args += ['-c', options.config_file] - -if options.eggs: - eggs_dir = os.path.abspath(os.path.expanduser(options.eggs)) -else: - eggs_dir = tempfile.mkdtemp() - -if options.setup_source is None: - if options.use_distribute: - options.setup_source = distribute_source - else: - options.setup_source = setuptools_source - -if options.accept_buildout_test_releases: - args.append('buildout:accept-buildout-test-releases=true') -args.append('bootstrap') - -try: - import pkg_resources - import setuptools # A flag. Sometimes pkg_resources is installed alone. - if not hasattr(pkg_resources, '_distribute'): - raise ImportError -except ImportError: - ez_code = urllib2.urlopen( - options.setup_source).read().replace('\r\n', '\n') - ez = {} - exec ez_code in ez - setup_args = dict(to_dir=eggs_dir, download_delay=0) - if options.download_base: - setup_args['download_base'] = options.download_base - if options.use_distribute: - setup_args['no_fake'] = True - ez['use_setuptools'](**setup_args) - if 'pkg_resources' in sys.modules: - reload(sys.modules['pkg_resources']) - import pkg_resources - # This does not (always?) update the default working set. We will - # do it. - for path in sys.path: - if path not in pkg_resources.working_set.entries: - pkg_resources.working_set.add_entry(path) - -cmd = [quote(sys.executable), - '-c', - quote('from setuptools.command.easy_install import main; main()'), - '-mqNxd', - quote(eggs_dir)] - -if not has_broken_dash_S: - cmd.insert(1, '-S') - -find_links = options.download_base -if not find_links: - find_links = os.environ.get('bootstrap-testing-find-links') -if find_links: - cmd.extend(['-f', quote(find_links)]) - -if options.use_distribute: - setup_requirement = 'distribute' -else: - setup_requirement = 'setuptools' -ws = pkg_resources.working_set -setup_requirement_path = ws.find( - pkg_resources.Requirement.parse(setup_requirement)).location -env = dict( - os.environ, - PYTHONPATH=setup_requirement_path) - -requirement = 'zc.buildout' -version = options.version -if version is None and not options.accept_buildout_test_releases: - # Figure out the most recent final version of zc.buildout. - import setuptools.package_index - _final_parts = '*final-', '*final' - - def _final_version(parsed_version): - for part in parsed_version: - if (part[:1] == '*') and (part not in _final_parts): - return False - return True - index = setuptools.package_index.PackageIndex( - search_path=[setup_requirement_path]) - if find_links: - index.add_find_links((find_links,)) - req = pkg_resources.Requirement.parse(requirement) - if index.obtain(req) is not None: - best = [] - bestv = None - for dist in index[req.project_name]: - distv = dist.parsed_version - if _final_version(distv): - if bestv is None or distv > bestv: - best = [dist] - bestv = distv - elif distv == bestv: - best.append(dist) - if best: - best.sort() - version = best[-1].version -if version: - requirement = '=='.join((requirement, version)) -cmd.append(requirement) - -if is_jython: - import subprocess - exitcode = subprocess.Popen(cmd, env=env).wait() -else: # Windows prefers this, apparently; otherwise we would prefer subprocess - exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env])) -if exitcode != 0: - sys.stdout.flush() - sys.stderr.flush() - print ("An error occurred when trying to install zc.buildout. " - "Look above this message for any errors that " - "were output by easy_install.") - sys.exit(exitcode) - -ws.add_entry(eggs_dir) -ws.require(requirement) -import zc.buildout.buildout -zc.buildout.buildout.main(args) -if not options.eggs: # clean up temporary egg directory - shutil.rmtree(eggs_dir) diff --git a/buildout.cfg b/buildout.cfg deleted file mode 100644 index b072afa..0000000 --- a/buildout.cfg +++ /dev/null @@ -1,25 +0,0 @@ -[buildout] -parts = python - django - -develop = . -eggs = django-uuslug - Django - -[versions] -django = 1.4.1 - -[python] -recipe = zc.recipe.egg -interpreter = python -eggs = ${buildout:eggs} - -[django] -recipe = djangorecipe -projectegg = uuslug -project = uuslug -settings = testsettings -test = uuslug -testrunner = test -eggs = ${buildout:eggs} - diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..819d703 --- /dev/null +++ b/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testsettings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/pushpi.sh b/pushpi.sh deleted file mode 100755 index 6de6046..0000000 --- a/pushpi.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -bin/buildout setup . sdist register upload - - diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1776e56 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Django>=1.3 +python-slugify>=0.0.2 + diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index f55b842..dc737bf --- a/setup.py +++ b/setup.py @@ -1,23 +1,82 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from setuptools import setup +import re import os -from distutils.core import setup +import sys -def read(fname): - return open(os.path.join(os.path.dirname(__file__), fname)).read() -setup(name='django-uuslug', - version='0.8', - description = "A Django slugify application that guarantees uniqueness and handles unicode", - long_description = read('README'), - author='Val Neekman', - author_email='val@neekware.com', - url='http://github.com/un33k/django-uuslug', - packages=['uuslug'], - install_requires = ['Unidecode>=0.04.5'], - classifiers=['Development Status :: 4 - Beta', - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Topic :: Utilities'], - ) +name = 'django-uuslug' +package = 'uuslug' +description = 'A Django slugify application that guarantees uniqueness and handles unicode.' +url = 'https://github.com/un33k/django-uuslug' +author = 'Val Neekman' +author_email = 'info@neekware.com' +license = 'BSD' +install_requires = ['python-slugify>=0.0.2'] +classifiers = [ + 'Development Status :: 4 - Beta', + 'Environment :: Web Environment', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Utilities' +] + +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/testsettings.py b/testsettings.py new file mode 100644 index 0000000..d5a8066 --- /dev/null +++ b/testsettings.py @@ -0,0 +1,8 @@ +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': ':memory:', + }, +} + +INSTALLED_APPS = ['uuslug'] diff --git a/uuslug/__init__.py b/uuslug/__init__.py index 8a52b2d..5f07dd6 100644 --- a/uuslug/__init__.py +++ b/uuslug/__init__.py @@ -1,80 +1,16 @@ # -*- coding: utf-8 -*- -import os -import pkg_resources -try: - _s = os.environ['DJANGO_SETTINGS_MODULE'] -except KeyError: - # DJANGO_SETTINGS_MODULE should have been set by now, if not, we must be in test mode - os.environ['DJANGO_SETTINGS_MODULE'] = 'uuslug.testsettings' +__version__ = '0.9.0' -import re -import unicodedata -from htmlentitydefs import name2codepoint -from django.utils.encoding import smart_unicode, force_unicode -from types import UnicodeType +from django.utils.encoding import smart_unicode +from slugify import slugify as pyslugify -pkg_resources.require("Unidecode") -from unidecode import unidecode - -# only allow the import of our public APIs (UU-SLUG = Uniqure & Unicode Slug) __all__ = ['slugify', 'uuslug'] - -# 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(s, entities=True, decimal=True, hexadecimal=True): - """ - make a slug from the given string - """ - if type(s) != UnicodeType: - s = unicode(s, 'utf-8', 'ignore') - - # decode now ( 影師嗎 = Ying Shi Ma) - s = unidecode(s) - - s = smart_unicode(s) - - #character entity reference - if entities: - s = CHAR_ENTITY_REXP.sub(lambda m: unichr(name2codepoint[m.group(1)]), s) - - #decimal character reference - if decimal: - try: - s = DECIMAL_REXP.sub(lambda m: unichr(int(m.group(1))), s) - except: - pass - - #hexadecimal character reference - if hexadecimal: - try: - s = HEX_REXP.sub(lambda m: unichr(int(m.group(1), 16)), s) - except: - pass - - #translate - s = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore') - - #replace unwanted characters - s = REPLACE1_REXP.sub('', s.lower()) # replace ' with nothing instead with - - s = REPLACE2_REXP.sub('-', s.lower()) - - #remove redundant - - s = REMOVE_REXP.sub('-', s).strip('-') - - return s +def slugify(text, entities=True, decimal=True, hexadecimal=True): + """ Make a slug from a given text """ + + return smart_unicode(pyslugify(text, entities, decimal, hexadecimal)) def uuslug(s, instance, entities=True, decimal=True, hexadecimal=True, @@ -113,7 +49,7 @@ def uuslug(s, instance, entities=True, decimal=True, hexadecimal=True, if hasattr(instance, 'objects'): raise Exception("Error: you must pass an instance to uuslug, not a model.") - queryset = instance.__class__.objects.all()#.only("pk", slug_field) + queryset = instance.__class__.objects.all() if filter_dict: queryset = queryset.filter(**filter_dict) if instance.pk: diff --git a/uuslug/models.py b/uuslug/models.py index a772a32..83d7cdb 100644 --- a/uuslug/models.py +++ b/uuslug/models.py @@ -1,11 +1,10 @@ import os # create a database table only in unit test mode -if os.environ['DJANGO_SETTINGS_MODULE'] == 'uuslug.testsettings': +if 'testsettings' in os.environ['DJANGO_SETTINGS_MODULE']: from django.db import models from uuslug import uuslug - class CoolSlug(models.Model): name = models.CharField(max_length=100) slug = models.CharField(max_length=200) diff --git a/uuslug/tests/test_uuslug.py b/uuslug/tests.py similarity index 90% rename from uuslug/tests/test_uuslug.py rename to uuslug/tests.py index 5c96b98..d818dec 100644 --- a/uuslug/tests/test_uuslug.py +++ b/uuslug/tests.py @@ -1,12 +1,6 @@ -# coding: utf-8 +# -*- coding: utf-8 -*- -"""Unit tests for uslug""" - -from django.db import models, connection -from django.template import Context, Template from django.test import TestCase -from django.db import connections, DEFAULT_DB_ALIAS, reset_queries -from django.core.signals import request_started # http://pypi.python.org/pypi/django-tools/ #from django_tools.unittest_utils.print_sql import PrintQueries @@ -34,11 +28,15 @@ class SlugUnicodeTestCase(TestCase): s = '影師嗎' r = slugify(s) self.assertEquals(r, "ying-shi-ma") - + + s = 'Компьютер' + r = slugify(s) + self.assertEquals(r, "kompiuter") class SlugUniqueTestCase(TestCase): """Tests for Slug - Unique""" + def test_manager(self): name = "john" @@ -58,7 +56,7 @@ class SlugUniqueTestCase(TestCase): self.assertEquals(obj.slug, "john-1") def test_start_no(self): - name = 'Foo Bar'#'C\'est déjà l\'été.' + name = 'Foo Bar' #with PrintQueries("create first 'Foo Bar'"): # display the SQL queries with self.assertNumQueries(2): @@ -83,3 +81,6 @@ class SlugUniqueTestCase(TestCase): # 4. query: INSERT values obj = AnotherSlug.objects.create(name=name) self.assertEquals(obj.slug, "foo-bar-3") + + + diff --git a/uuslug/tests/__init__.py b/uuslug/tests/__init__.py deleted file mode 100644 index f1cb9fb..0000000 --- a/uuslug/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from uuslug.tests.test_uuslug import * diff --git a/uuslug/testsettings.py b/uuslug/testsettings.py deleted file mode 100644 index 85da75f..0000000 --- a/uuslug/testsettings.py +++ /dev/null @@ -1,17 +0,0 @@ -import os - -DEBUG = TEMPLATE_DEBUG = True - -PROJECT_NAME = "uuslug" - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': PROJECT_NAME.strip().split(".")[0]+"_db" - } -} -INSTALLED_APPS = [ - 'uuslug', -] - -