A lightweight, type-safe Python library for emoji search, transformation, and detection.
Project description
pymojis
A tiny Python library for working with emojis. Look them up by name or codepoint, pick a random one, check if a string contains any, pull all the emojis out of a chat message, convert to HTML or to a Twemoji URL โ that kind of thing.
I wrote it because every time I needed "just a list of emojis with their names" I ended up vendoring a JSON file from somewhere. This is that JSON file with a few helpers on top, no runtime dependencies, and types that actually work in mypy strict.
Install
pip install pymojis # ~1900 emojis, ~230 KB of data
pip install 'pymojis[full]' # full Unicode set, ~3790 emojis (~1 MB)
Python 3.12+.
The default dataset covers the everyday stuff. The [full] extra pulls
in pymojis-fulldata, which ships every emoji including the skin-tone
variants, ZWJ sequences, CLDR keywords, and GitHub-style shortcodes โ
useful if you care about completeness, overkill if you don't.
Using it
from pymojis import PymojisManager
m = PymojisManager()
m.get_random(length=3) # 3 random Emoji objects
m.get_by_name("grinning face with smiling eyes") # โ '๐'
m.get_by_code("1F604") # โ '๐'
m.get_by_emoji("๐") # โ Emoji(...)
m.contains_emojis("hello ๐") # โ True
m.is_emoji("๐") # โ True
m.extract("hi ๐ and ๐ช") # โ [Emoji('grinning face'), Emoji('sleepy face')]
m.strip("hi ๐ there") # โ "hi there"
m.replace("hi ๐", lambda e: f"[{e.name}]") # โ "hi [grinning face]"
m.flag_for("FR") # โ '๐ซ๐ท'
m.country_of("๐ซ๐ท") # โ 'FR'
m.to_html("๐ตโ๐ซ") # โ "😵‍💫"
m.to_unicode_escape("๐") # โ "\\U0001F600"
m.to_image_url("๐") # โ twemoji CDN URL
Switching to the full set:
m = PymojisManager(use_full_dataset=True)
# Raises DatasetNotFoundError (with the pip command) if [full] isn't installed.
What's on the manager
Lookup and random
get_random(categories=None, length=1, exclude=None)โ random pick.categorieswins overexclude.get_all_emojis(exclude=None)โ every emoji.excludeaccepts"complex"(drop multi-codepoint) or a list of categories.get_by_code(code),get_by_name(name)โ single-codepoint / full-name lookup, case-insensitive.get_by_category(category),get_by_subcategory(name),get_by_emoji(emoji).categories(),sub_categories(category=None)โ list what the dataset actually contains.
Detection and scanning โ all share one longest-match-first regex,
so ๐๐ฝ matches as one unit, not "thumbs up + skin tone modifier".
contains_emojis(text)/is_emoji(text).extract(text)โlist[Emoji]in order of appearance.find(text)โ iterator of(emoji, start, end).count(text),count_by(text).strip(text),replace(text, repl)โreplis a literal string orCallable[[Emoji], str].demojifie(text)โ rewrites each emoji as:slugified_name:.
Format and convert
to_html(emoji)โ&#xHEX;references.to_codepoint_string(emoji, sep=" ", prefix="U+")โ"U+1F635 U+200D U+1F4AB".to_unicode_escape(emoji)โ\U0001F600form, safe to paste into Python source.to_image_url(emoji, provider="twemoji" | "openmoji", extension="svg" | "png")โ public CDN URLs.
Flags โ these don't need any dataset, they're computed from codepoints.
is_flag(emoji)โ Flags category or a bare Regional Indicator Symbol pair.flag_for(country_code)โ"FR"โ"๐ซ๐ท". Raises on invalid input.country_of(emoji)โ"๐ซ๐ท"โ"FR".Nonefor non-flags (including tag-sequence subdivision flags).
Family and shortcodes โ only meaningful with the [full] dataset.
On the light bundle these return None / [] because the data isn't
there (size budget), not because the logic is missing.
base_of(emoji)โ"๐๐ฝ"โ"๐".skin_tones(emoji)โ all five Fitzpatrick siblings; works whether you pass the base or a variant.to_shortcode(emoji, set_name="github"),from_shortcode(code, set_name=None).
Search
search(query, limit=10)โ ranked: exact name, then name substring, then name token, then keyword. Falls back to name-only on the light dataset (no keywords).suggest(emoji, limit=5)โ same subcategory first, then keyword overlap, then same category.
Text replacement based on names
emojifie(text)โ replaces whole words with an emoji whose name contains that word (see below).
Anything that takes a string and gets something else raises TypeError
on the spot. No silent None, no warnings to ignore.
Categories
from pymojis import Categories
# Literal type of: "Smileys & Emotion", "People & Body", "Animals & Nature",
# "Food & Drink", "Activities", "Travel & Places", "Objects", "Symbols",
# "Flags", "Component"
The Emoji record
Emoji(
id=..., # UUID, generated on construction
emoji="๐๐ฝ",
name="thumbs up: medium skin tone",
code=["1F44D", "1F3FD"], # list because ZWJ sequences have multiple codepoints
category="People & Body",
sub_category="hand-fingers-closed",
unicode_version="1.0",
qualification="fully-qualified", # or "minimally-qualified", "unqualified", "component"
base_code=["1F44D"], # None for base emojis; set for skin-tone variants
keywords=["hand", "thumb", "up"], # full dataset only; [] on light
shortcodes={"github": ":thumbs_up_..."}, # full dataset only; {} on light
)
unicode_version, qualification, and base_code come from
emoji-test.txt; keywords and shortcodes are added from the CLDR
annotations during the dataset build (see scripts/build_dataset.py).
CLI
There's a small command-line tool โ call it as pymojis once installed,
or python -m pymojis without installing the script.
pymojis search grin # find emojis by name / keyword
pymojis search face --limit 5
pymojis random --length 3 # 3 random emoji glyphs
pymojis random --length 1 -v # verbose: emoji + name + category
pymojis info ๐ # everything we know about an emoji
pymojis --full search "thumbs" # use the full dataset for richer search
Exit codes: 0 on success, 1 when nothing matched (or the emoji's
unknown), 2 when argparse didn't like your invocation.
A note on emojifie
It's whole-word matching against a token index built from emoji names. Tokens shorter than 3 characters are skipped โ otherwise "I" and "m" would get eaten by โน and โ, which is funny exactly once. If a word matches several emojis, the first one in dataset order wins.
m.emojifie("I'm sleepy") # โ "I'm ๐ช"
m.emojifie("zzzz xyzzy") # โ "zzzz xyzzy"
It still only looks at words that appear in the emoji name, so
"happy" doesn't match ๐ โ for keyword-aware search use m.search
instead (with the [full] dataset, which ships the CLDR keywords).
Development
There's a Makefile for the usual chores:
make install # uv sync --extra dev --frozen
make lint
make format-check
make typecheck
make test
make build # builds both wheels
make ci # everything above
pre-commit is set up โ uv run pre-commit install once and the same
checks run on commit.
Contributing
Issues and PRs welcome. Run make ci before opening one.
License
MIT โ see LICENSE.
The bundled emoji metadata is generated directly from the
Unicode CLDR sources โ emoji-test.txt (UTS #51)
for the canonical emoji list and annotations/en.xml for keywords. Both are
vendored under third_party/cldr/ and licensed under the Unicode License V3
(see third_party/cldr/LICENSE and third_party/NOTICE.txt).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pymojis-1.1.0.tar.gz.
File metadata
- Download URL: pymojis-1.1.0.tar.gz
- Upload date:
- Size: 57.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8e5edeed8cb13ab54bf8cd2db6693151c44fea76c3410d7492100198b669c48
|
|
| MD5 |
f517b0ba07e21e3054b4245c5fd759af
|
|
| BLAKE2b-256 |
72903fcdca050b6f79a6f4c3ab6ce09146f9f35f58a9a7ae6451caff145c131a
|
Provenance
The following attestation bundles were made for pymojis-1.1.0.tar.gz:
Publisher:
github_ci.yaml on pallandir/pymojis
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymojis-1.1.0.tar.gz -
Subject digest:
b8e5edeed8cb13ab54bf8cd2db6693151c44fea76c3410d7492100198b669c48 - Sigstore transparency entry: 1523499005
- Sigstore integration time:
-
Permalink:
pallandir/pymojis@cd95e633f58f4872e7f355d42e580c8f6d256645 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/pallandir
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
github_ci.yaml@cd95e633f58f4872e7f355d42e580c8f6d256645 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pymojis-1.1.0-py3-none-any.whl.
File metadata
- Download URL: pymojis-1.1.0-py3-none-any.whl
- Upload date:
- Size: 57.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0bba36ecdd68bc1005cdcb5072f2ea62910b183b7ac23abd22ee3efaeddd6ff3
|
|
| MD5 |
d14958dbe2e700cbd458a059a6b17f10
|
|
| BLAKE2b-256 |
589b7911f640a3ca38f03e9b7277bddf770fd7e6bc9050cbe3ca23e31b5bf97c
|
Provenance
The following attestation bundles were made for pymojis-1.1.0-py3-none-any.whl:
Publisher:
github_ci.yaml on pallandir/pymojis
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymojis-1.1.0-py3-none-any.whl -
Subject digest:
0bba36ecdd68bc1005cdcb5072f2ea62910b183b7ac23abd22ee3efaeddd6ff3 - Sigstore transparency entry: 1523499033
- Sigstore integration time:
-
Permalink:
pallandir/pymojis@cd95e633f58f4872e7f355d42e580c8f6d256645 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/pallandir
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
github_ci.yaml@cd95e633f58f4872e7f355d42e580c8f6d256645 -
Trigger Event:
push
-
Statement type: