├── .gitignore ├── bin └── slugify ├── src └── slugify.py ├── setup.py ├── UNLICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info/ 2 | *.pyc 3 | *.pyo 4 | .DS_Store 5 | MANIFEST 6 | build/ 7 | dist/ 8 | -------------------------------------------------------------------------------- /bin/slugify: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | USAGE = """Usage: echo "Héllo Wörld" | slugify # => hello-world""" 5 | 6 | import sys 7 | import slugify 8 | 9 | if sys.argv[1:]: 10 | print >>sys.stderr, USAGE 11 | sys.exit(1) 12 | 13 | line = sys.stdin.readline() 14 | while line: 15 | print slugify.slugify(line.decode(sys.stdin.encoding)) 16 | line = sys.stdin.readline() 17 | -------------------------------------------------------------------------------- /src/slugify.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """A generic slugifier utility (currently only for Latin-based scripts).""" 4 | 5 | import re 6 | import unicodedata 7 | 8 | __version__ = '0.0.1' 9 | 10 | 11 | def slugify(string): 12 | 13 | """ 14 | Slugify a unicode string. 15 | 16 | Example: 17 | 18 | >>> slugify(u"Héllø Wörld") 19 | u"hello-world" 20 | 21 | """ 22 | 23 | return re.sub(r'[-\s]+', '-', 24 | unicode( 25 | re.sub(r'[^\w\s-]', '', 26 | unicodedata.normalize('NFKD', string) 27 | .encode('ascii', 'ignore')) 28 | .strip() 29 | .lower())) 30 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import re 6 | 7 | from distutils.core import setup 8 | 9 | 10 | rel_file = lambda *args: os.path.join(os.path.dirname(os.path.abspath(__file__)), *args) 11 | 12 | def read_from(filename): 13 | fp = open(filename) 14 | try: 15 | return fp.read() 16 | finally: 17 | fp.close() 18 | 19 | def get_version(): 20 | data = read_from(rel_file('src', 'slugify.py')) 21 | return re.search(r"__version__ = '([^']+)'", data).group(1) 22 | 23 | 24 | setup( 25 | name = 'slugify', 26 | version = get_version(), 27 | author = "Zachary Voase", 28 | author_email = "z@zacharyvoase.com", 29 | url = 'http://github.com/zacharyvoase/slugify', 30 | description = "A generic slugifier.", 31 | py_modules = ['slugify'], 32 | package_dir = {'': 'src'}, 33 | scripts = ['bin/slugify'], 34 | ) 35 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `slugify` 2 | 3 | `slugify` is a generic slugifier utility, inspired by Django’s `slugify` 4 | template filter, packaged as a standalone Python library and command-line 5 | application. 6 | 7 | 8 | ## Installation 9 | 10 | % [sudo] pip install slugify # OR 11 | % [sudo] easy_install slugify 12 | 13 | 14 | ## Usage 15 | 16 | ### Python 17 | 18 | >>> import slugify 19 | >>> slugify.slugify(u"Héllø Wörld") 20 | u"hello-world" 21 | 22 | 23 | ### Command-line 24 | 25 | % echo "Héllø Wörld" | slugify 26 | hello-world 27 | 28 | 29 | ## License 30 | 31 | The parts of this application that I wrote are released into the public domain 32 | via [The Unlicense](http://unlicense.org/). 33 | 34 | The slugifying routine is derived from Django, and as such is BSD-licensed: 35 | 36 | > Copyright (c) Django Software Foundation and individual contributors. 37 | > All rights reserved. 38 | > 39 | > Redistribution and use in source and binary forms, with or without modification, 40 | > are permitted provided that the following conditions are met: 41 | > 42 | > 1. Redistributions of source code must retain the above copyright notice, 43 | > this list of conditions and the following disclaimer. 44 | > 45 | > 2. Redistributions in binary form must reproduce the above copyright 46 | > notice, this list of conditions and the following disclaimer in the 47 | > documentation and/or other materials provided with the distribution. 48 | > 49 | > 3. Neither the name of Django nor the names of its contributors may be used 50 | > to endorse or promote products derived from this software without 51 | > specific prior written permission. 52 | > 53 | > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 54 | > ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 55 | > WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 56 | > DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 57 | > ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 58 | > (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 59 | > LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 60 | > ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 61 | > (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 62 | > SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 63 | --------------------------------------------------------------------------------