├── emojiencoding ├── __init__.py ├── bootstrap.py └── emoji.py ├── .gitignore ├── emoji.pth ├── MANIFEST.in ├── example.py ├── setup.cfg ├── post_setup.py ├── README.md └── setup.py /emojiencoding/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .DS_Store 3 | *.egg-info 4 | -------------------------------------------------------------------------------- /emoji.pth: -------------------------------------------------------------------------------- 1 | import sys; from emojiencoding import bootstrap 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include post_setup.py 2 | include README.md 3 | include emoji.pth 4 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: emoji -*- 2 | def 📢(✉️): 3 | print(✉️) 4 | 5 | 📢("✋ 🌏") 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | # This flag says that the code is written to work on both Python 2 and Python 3 | # 3. If at all possible, it is good practice to do this. If you cannot, you 4 | # will need to generate wheels for each Python version that you support. 5 | universal=1 6 | -------------------------------------------------------------------------------- /emojiencoding/bootstrap.py: -------------------------------------------------------------------------------- 1 | from . import emoji 2 | 3 | import sys, codecs 4 | if sys.stdout.encoding != 'emoji': 5 | sys.stdout = codecs.getwriter('emoji')(sys.stdout.buffer, 'strict') 6 | if sys.stderr.encoding != 'emoji': 7 | sys.stderr = codecs.getwriter('emoji')(sys.stderr.buffer, 'strict') 8 | -------------------------------------------------------------------------------- /post_setup.py: -------------------------------------------------------------------------------- 1 | from distutils.sysconfig import get_python_lib 2 | from shutil import copyfile 3 | from os import path 4 | 5 | def main(): 6 | here = path.abspath(path.dirname(__file__)) 7 | print("Copying 'emoji.pth' to %s" % get_python_lib()) 8 | copyfile( 9 | path.join(here, 'emoji.pth'), 10 | path.join(get_python_lib(), 'emoji.pth') 11 | ) 12 | 13 | if __name__ == '__main__': 14 | main() 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Emoji encoding for 🐍 2 | 3 | This module provides a custom source code encoding allowing usage of Emoji's for things like variable names or function names. 4 | 5 | ## Usage 6 | 7 | Install the package with pip: 8 | ``` 9 | pip install emoji-encoding 10 | ``` 11 | **It's recommended to install it in [a virtualenv](https://virtualenv.readthedocs.org/en/latest/)** 12 | 13 | After installation you can specify the encoding in the beginning of a Python file: 14 | 15 | ```python 16 | # -*- coding: emoji -*- 17 | def 📢(✉️): 18 | print(✉️) 19 | 20 | 📢("✋ 🌏") 21 | ``` 22 | 23 | ## Uninstalling 24 | 25 | This package will create `emoji.pth` file in your `site-packages` directory to autoload the codec. After removing the module you need to remove this file manually. 26 | 27 | ## Known issues 28 | 29 | Currently the encoding is only available in imported modules so trying to run Emoji encoded file directly will fail: 30 | 31 | ``` 32 | $ python emoji.py 33 | File "emoji.py", line 1 34 | SyntaxError: encoding problem: emoji 35 | ``` 36 | 37 | Easy workaround is to have an another file that imports the Emoji encoded file: 38 | 39 | ```shell 40 | $ cat bootstrap.py 41 | import emoji 42 | $ python bootstrap.py 43 | ✋ 🌏 44 | ``` 45 | 46 | ## History 47 | 48 | It all started with Ola Sendecka's talk about [Emoji Driven Development](https://speakerdeck.com/jezdezcon/ola-sendecka-emoji-driven-development) which made me wonder: "why *can't* we use Emoji's in Python?". After a bit of hacking I was able to use them [with a patched cpython](https://twitter.com/suda/status/614814994367168512). This wasn't a perfect solution so playing with this idea I ended up with custom [codec](https://docs.python.org/3/library/codecs.html) that translates Emoji's to their ASCII representations and adds prefix/suffix to decode such unique string back to Unicode. 49 | -------------------------------------------------------------------------------- /emojiencoding/emoji.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import emote 3 | import re 4 | 5 | decoding_prefix = "emoji_start_" 6 | decoding_sufix = "_emoji_end" 7 | # TODO: Make regex non greedy 8 | decoding_pattern = re.compile("%s([\w\-]+)%s" % (decoding_prefix, decoding_sufix)) 9 | try: 10 | # UCS-4 11 | highpoints = re.compile(u'([\U00002600-\U000027BF])|([\U0001f300-\U0001f64F])|([\U0001f680-\U0001f6FF])') 12 | except re.error: 13 | # UCS-2 14 | highpoints = re.compile(u'([\u2600-\u27BF])|([\uD83C][\uDF00-\uDFFF])|([\uD83D][\uDC00-\uDE4F])|([\uD83D][\uDE80-\uDEFF])') 15 | 16 | class EmojiCodec(codecs.Codec): 17 | def encode(self, input, errors='strict'): 18 | emojis = decoding_pattern.finditer(input) 19 | for emoji in emojis: 20 | emoji_string = emoji.group(1) 21 | input = input.replace(emoji.group(0), emote.lookup(emoji_string)) 22 | return (input.encode('utf8'), len(input)) 23 | 24 | def decode(self, input, errors='strict'): 25 | input_string = codecs.decode(input, 'utf8') 26 | emojis = highpoints.finditer(input_string) 27 | 28 | for emoji in emojis: 29 | emoji_string = emoji.group(0) 30 | substitute = "%s%s%s" % (decoding_prefix, emote.decode(emoji_string), decoding_sufix) 31 | input_string = input_string.replace(emoji_string, substitute) 32 | return (input_string, len(input)) 33 | 34 | class EmojiIncrementalEncoder(codecs.IncrementalEncoder): 35 | def encode(self, input, final=False): 36 | return EmojiCodec().encode(input) 37 | 38 | class EmojiIncrementalDecoder(codecs.IncrementalDecoder): 39 | def decode(self, input, final=False): 40 | return EmojiCodec().decode(input) 41 | 42 | class EmojiStreamReader(EmojiCodec, codecs.StreamReader): 43 | pass 44 | 45 | class EmojiStreamWriter(EmojiCodec, codecs.StreamWriter): 46 | pass 47 | 48 | def search(encoding): 49 | if encoding == "emoji": 50 | return codecs.CodecInfo( 51 | name='emoji', 52 | encode=EmojiCodec().encode, 53 | decode=EmojiCodec().decode, 54 | incrementalencoder=EmojiIncrementalEncoder, 55 | incrementaldecoder=EmojiIncrementalDecoder, 56 | streamreader=EmojiStreamReader, 57 | streamwriter=EmojiStreamWriter, 58 | ) 59 | return None 60 | 61 | codecs.register(search) 62 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """A setuptools based setup module. 2 | 3 | See: 4 | https://packaging.python.org/en/latest/distributing.html 5 | https://github.com/pypa/sampleproject 6 | """ 7 | 8 | # Always prefer setuptools over distutils 9 | from setuptools import setup, find_packages 10 | from setuptools.command.install import install as _install 11 | from setuptools.command.develop import develop as _develop 12 | # To use a consistent encoding 13 | from codecs import open 14 | from os import path 15 | 16 | here = path.abspath(path.dirname(__file__)) 17 | 18 | from post_setup import main as post_install 19 | 20 | class CustomInstall(_install): 21 | def run(self): 22 | _install.run(self) 23 | post_install() 24 | 25 | class CustomDevelop(_develop): 26 | def run(self): 27 | _develop.run(self) 28 | post_install() 29 | 30 | # Get the long description from the README file 31 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 32 | long_description = f.read() 33 | 34 | if __name__ == '__main__': 35 | setup( 36 | name='emoji-encoding', 37 | 38 | # Versions should comply with PEP440. For a discussion on single-sourcing 39 | # the version across setup.py and the project code, see 40 | # https://packaging.python.org/en/latest/single_source_version.html 41 | version='0.0.5', 42 | 43 | description='Module providing Emoji encoding for Python', 44 | long_description=long_description, 45 | 46 | # The project's main homepage. 47 | url='https://github.com/suda/python-emoji-encoding', 48 | 49 | # Author details 50 | author='Wojtek Siudzinski', 51 | author_email='admin@suda.pl', 52 | 53 | # Choose your license 54 | license='MIT', 55 | 56 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 57 | classifiers=[ 58 | # How mature is this project? Common values are 59 | # 3 - Alpha 60 | # 4 - Beta 61 | # 5 - Production/Stable 62 | 'Development Status :: 3 - Alpha', 63 | 64 | # Indicate who your project is intended for 65 | 'Intended Audience :: Developers', 66 | 'Topic :: Software Development :: Build Tools', 67 | 68 | # Pick your license as you wish (should match "license" above) 69 | 'License :: OSI Approved :: MIT License', 70 | 71 | # Specify the Python versions you support here. In particular, ensure 72 | # that you indicate whether you support Python 2, Python 3 or both. 73 | 'Programming Language :: Python :: 2', 74 | 'Programming Language :: Python :: 2.6', 75 | 'Programming Language :: Python :: 2.7', 76 | 'Programming Language :: Python :: 3', 77 | 'Programming Language :: Python :: 3.2', 78 | 'Programming Language :: Python :: 3.3', 79 | 'Programming Language :: Python :: 3.4', 80 | 'Programming Language :: Python :: 3.5', 81 | ], 82 | 83 | # What does your project relate to? 84 | keywords='emoji codec encoding', 85 | 86 | # You can just specify the packages manually here if your project is 87 | # simple. Or you can use find_packages(). 88 | # packages=find_packages(exclude=['contrib', 'docs', 'tests']), 89 | packages=find_packages(), 90 | 91 | # Alternatively, if you want to distribute just a my_module.py, uncomment 92 | # this: 93 | # py_modules=["emojicodec"], 94 | 95 | # List run-time dependencies here. These will be installed by pip when 96 | # your project is installed. For an analysis of "install_requires" vs pip's 97 | # requirements files see: 98 | # https://packaging.python.org/en/latest/requirements.html 99 | install_requires=['emote'], 100 | 101 | # List additional groups of dependencies here (e.g. development 102 | # dependencies). You can install these using the following syntax, 103 | # for example: 104 | # $ pip install -e .[dev,test] 105 | extras_require={ 106 | 'dev': ['check-manifest'], 107 | 'test': ['coverage'], 108 | }, 109 | 110 | cmdclass={ 111 | 'install': CustomInstall, 112 | 'develop': CustomDevelop, 113 | }, 114 | ) 115 | --------------------------------------------------------------------------------