Skip to content

Commit fe245b0

Browse files
committed
Initial commit
0 parents  commit fe245b0

File tree

10 files changed

+258
-0
lines changed

10 files changed

+258
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
__pycache__
2+
.DS_Store
3+
*.egg-info

README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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.

emoji.pth

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import sys; from emojiencoding import bootstrap

emojiencoding/__init__.py

Whitespace-only changes.

emojiencoding/bootstrap.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
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')

emojiencoding/emoji.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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)

example.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# -*- coding: emoji -*-
2+
def 📢(✉️):
3+
print(✉️)
4+
5+
📢("✋ 🌏")

post_setup.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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()

setup.cfg

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
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

setup.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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.1',
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='[email protected]',
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+
)

0 commit comments

Comments
 (0)