pymojis 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymojis-1.0.1/LICENSE +21 -0
- pymojis-1.0.1/PKG-INFO +220 -0
- pymojis-1.0.1/README.md +177 -0
- pymojis-1.0.1/pyproject.toml +148 -0
- pymojis-1.0.1/setup.cfg +4 -0
- pymojis-1.0.1/src/pymojis/__init__.py +5 -0
- pymojis-1.0.1/src/pymojis/application/__init__.py +0 -0
- pymojis-1.0.1/src/pymojis/application/pymojis_manager.py +103 -0
- pymojis-1.0.1/src/pymojis/domain/__init__.py +0 -0
- pymojis-1.0.1/src/pymojis/domain/entities/__init__.py +0 -0
- pymojis-1.0.1/src/pymojis/domain/entities/emojis.py +66 -0
- pymojis-1.0.1/src/pymojis/domain/repositories/__init__.py +0 -0
- pymojis-1.0.1/src/pymojis/domain/repositories/repository.py +43 -0
- pymojis-1.0.1/src/pymojis/infrastructure/__init__.py +0 -0
- pymojis-1.0.1/src/pymojis/infrastructure/data/__init__.py +0 -0
- pymojis-1.0.1/src/pymojis/infrastructure/data/emoji_data.json +9828 -0
- pymojis-1.0.1/src/pymojis/infrastructure/data_loader/__init__.py +0 -0
- pymojis-1.0.1/src/pymojis/infrastructure/data_loader/emojis_loader.py +33 -0
- pymojis-1.0.1/src/pymojis/infrastructure/data_loader/file_loader.py +24 -0
- pymojis-1.0.1/src/pymojis/infrastructure/exceptions.py +2 -0
- pymojis-1.0.1/src/pymojis/infrastructure/pymojis_repository.py +161 -0
- pymojis-1.0.1/src/pymojis/infrastructure/utils.py +11 -0
- pymojis-1.0.1/src/pymojis/py.typed +0 -0
- pymojis-1.0.1/src/pymojis.egg-info/PKG-INFO +220 -0
- pymojis-1.0.1/src/pymojis.egg-info/SOURCES.txt +26 -0
- pymojis-1.0.1/src/pymojis.egg-info/dependency_links.txt +1 -0
- pymojis-1.0.1/src/pymojis.egg-info/requires.txt +18 -0
- pymojis-1.0.1/src/pymojis.egg-info/top_level.txt +1 -0
pymojis-1.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pallandir
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pymojis-1.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pymojis
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: A lightweight, type-safe Python library for emoji search, transformation, and detection.
|
|
5
|
+
Author-email: Pallandir <pallandir2@gmail.com>
|
|
6
|
+
Maintainer-email: Pallandir <pallandir2@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/pallandir/pymojis
|
|
9
|
+
Project-URL: Repository, https://github.com/pallandir/pymojis
|
|
10
|
+
Project-URL: Issues, https://github.com/pallandir/pymojis/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/pallandir/pymojis/releases
|
|
12
|
+
Keywords: emoji,unicode,text,utilities
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: Text Processing
|
|
21
|
+
Classifier: Topic :: Utilities
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Classifier: Natural Language :: English
|
|
24
|
+
Requires-Python: >=3.12
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Provides-Extra: full
|
|
28
|
+
Requires-Dist: pymojis-fulldata==1.0.1; extra == "full"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: build~=1.2; extra == "dev"
|
|
31
|
+
Requires-Dist: pre-commit~=4.2; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest~=8.4; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov~=6.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-mock~=3.10; extra == "dev"
|
|
35
|
+
Requires-Dist: twine~=6.1; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy~=1.0; extra == "dev"
|
|
37
|
+
Requires-Dist: ruff~=0.8; extra == "dev"
|
|
38
|
+
Provides-Extra: test
|
|
39
|
+
Requires-Dist: pytest~=8.4; extra == "test"
|
|
40
|
+
Requires-Dist: pytest-cov~=6.0; extra == "test"
|
|
41
|
+
Requires-Dist: pytest-mock~=3.10; extra == "test"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# pymojis
|
|
45
|
+
|
|
46
|
+
[](https://pypi.org/project/pymojis/)
|
|
47
|
+
[](https://pypi.org/project/pymojis/)
|
|
48
|
+
[](https://github.com/pallandir/pymojis/blob/main/LICENSE)
|
|
49
|
+
[](https://github.com/pallandir/pymojis/actions/workflows/github_ci.yaml)
|
|
50
|
+
|
|
51
|
+
A small, type-safe Python library for working with emojis: search them by
|
|
52
|
+
name / code / character, transform text, detect emojis in strings, and
|
|
53
|
+
convert to HTML.
|
|
54
|
+
|
|
55
|
+
- **Zero runtime dependencies.**
|
|
56
|
+
- **Two install sizes**: a tiny default (~230 KB of data) or the full
|
|
57
|
+
Unicode dataset (~1 MB) via the `[full]` extra.
|
|
58
|
+
- **Strict typing** — `py.typed`, full mypy strict compliance.
|
|
59
|
+
- **Fail-fast API** — bad input raises immediately, no silent `None`
|
|
60
|
+
returns or warnings.
|
|
61
|
+
|
|
62
|
+
## Install
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install pymojis # lightweight: ~1900 emojis
|
|
66
|
+
pip install 'pymojis[full]' # full Unicode coverage: ~3790 emojis
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Requires **Python 3.12+**.
|
|
70
|
+
|
|
71
|
+
## Quick start
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from pymojis import PymojisManager
|
|
75
|
+
|
|
76
|
+
manager = PymojisManager()
|
|
77
|
+
|
|
78
|
+
# Pick random emojis
|
|
79
|
+
print([e.emoji for e in manager.get_random(length=3)])
|
|
80
|
+
# → ['😊', '🎉', '🌟']
|
|
81
|
+
|
|
82
|
+
# Look up by name / code / character
|
|
83
|
+
manager.get_by_name("grinning face with smiling eyes") # → '😄'
|
|
84
|
+
manager.get_by_code("1F604") # → '😄'
|
|
85
|
+
manager.get_by_emoji("😊") # → Emoji(...)
|
|
86
|
+
|
|
87
|
+
# Replace whole-word matches in text
|
|
88
|
+
manager.emojifie("I'm sleepy")
|
|
89
|
+
# → "I'm 😪"
|
|
90
|
+
|
|
91
|
+
# Detection
|
|
92
|
+
manager.contains_emojis("hello 👋") # → True
|
|
93
|
+
manager.is_emoji("😄") # → True
|
|
94
|
+
manager.is_emoji("😄😊") # → False
|
|
95
|
+
|
|
96
|
+
# HTML hex references
|
|
97
|
+
manager.to_html("😵💫")
|
|
98
|
+
# → "😵‍💫"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
To use the full Unicode dataset:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
manager = PymojisManager(use_full_dataset=True)
|
|
105
|
+
# Requires `pip install 'pymojis[full]'`. Raises DatasetNotFoundError
|
|
106
|
+
# with installation instructions otherwise.
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## API
|
|
110
|
+
|
|
111
|
+
| Method | Returns | Notes |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `get_random(categories=None, length=1, exclude=None)` | `list[Emoji]` | `categories` takes precedence over `exclude`. |
|
|
114
|
+
| `get_all_emojis(exclude=None)` | `list[Emoji]` | `exclude` accepts `"complex"` or a list of categories. |
|
|
115
|
+
| `get_by_code(code)` | `str \| None` | Single-codepoint lookup. Case-insensitive. |
|
|
116
|
+
| `get_by_name(name)` | `str \| None` | Full-name lookup. Case-insensitive. |
|
|
117
|
+
| `get_by_category(category)` | `list[str]` | All emojis in a category. |
|
|
118
|
+
| `get_by_emoji(emoji)` | `Emoji \| None` | Reverse lookup from character to record. |
|
|
119
|
+
| `contains_emojis(text)` | `bool` | True if `text` contains at least one known emoji. |
|
|
120
|
+
| `is_emoji(text)` | `bool` | True if `text.strip()` is a single known emoji. |
|
|
121
|
+
| `emojifie(text)` | `str` | Replace whole words with emojis (whose name *contains* that word). |
|
|
122
|
+
| `to_html(emoji)` | `str` | Encode each codepoint as `&#xHEX;`. |
|
|
123
|
+
|
|
124
|
+
All methods raise `TypeError` on non-`str` arguments — no silent `None`.
|
|
125
|
+
|
|
126
|
+
### Categories
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from pymojis import Categories
|
|
130
|
+
|
|
131
|
+
Categories # type alias of all valid categories:
|
|
132
|
+
# "Smileys & Emotion", "People & Body", "Animals & Nature",
|
|
133
|
+
# "Food & Drink", "Activities", "Travel & Places", "Objects",
|
|
134
|
+
# "Symbols", "Flags", "Component"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### `Emoji` data model
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from pymojis import Emoji
|
|
141
|
+
|
|
142
|
+
@dataclass-like
|
|
143
|
+
class Emoji:
|
|
144
|
+
id: str # auto-generated UUID
|
|
145
|
+
emoji: str # the character itself, e.g. "😄"
|
|
146
|
+
name: str # e.g. "grinning face with smiling eyes"
|
|
147
|
+
code: list[str] # one or more Unicode codepoints, e.g. ["1F604"]
|
|
148
|
+
# (multi-codepoint emojis like ZWJ sequences have len > 1)
|
|
149
|
+
category: str # e.g. "Smileys & Emotion"
|
|
150
|
+
sub_category: str # e.g. "face-smiling"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Notes on `emojifie`
|
|
154
|
+
|
|
155
|
+
`emojifie` does whole-word matching. Each word in the input is looked up in
|
|
156
|
+
an index of emoji-name tokens. Tokens shorter than 3 characters are skipped
|
|
157
|
+
(otherwise pronouns like "I" and "m" would be replaced by ℹ and Ⓜ). When a
|
|
158
|
+
word matches multiple emojis, the first one (in dataset order) wins.
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
manager.emojifie("I'm sleepy") # → "I'm 😪" (sleepy → "sleepy face")
|
|
162
|
+
manager.emojifie("zzzz xyzzy") # → "zzzz xyzzy" (no match, unchanged)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Releases (maintainer notes)
|
|
166
|
+
|
|
167
|
+
This repo publishes **two** packages on every git tag: `pymojis` and
|
|
168
|
+
`pymojis-fulldata`. Both must share the same version.
|
|
169
|
+
|
|
170
|
+
To cut a release:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# 1. Bump version in BOTH pyproject files
|
|
174
|
+
$EDITOR pyproject.toml # version = "X.Y.Z"
|
|
175
|
+
$EDITOR packages/pymojis-fulldata/pyproject.toml # version = "X.Y.Z"
|
|
176
|
+
# Also bump the [full] extra dependency pin in pyproject.toml.
|
|
177
|
+
|
|
178
|
+
# 2. Commit, tag, push
|
|
179
|
+
git commit -am "Release vX.Y.Z"
|
|
180
|
+
git tag vX.Y.Z
|
|
181
|
+
git push origin main --tags
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
CI runs the full `make ci` pipeline (lint, format, typecheck, tests,
|
|
185
|
+
build both wheels, twine check) and publishes via PyPI Trusted Publishing
|
|
186
|
+
(OIDC — no API tokens). One-time setup: register both projects as
|
|
187
|
+
Trusted Publishers on https://pypi.org pointing at this repo and the
|
|
188
|
+
workflow `.github/workflows/github_ci.yaml`.
|
|
189
|
+
|
|
190
|
+
## Development
|
|
191
|
+
|
|
192
|
+
A `Makefile` wraps the common workflows:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
make install # uv sync --extra dev --frozen
|
|
196
|
+
make lint # ruff check
|
|
197
|
+
make format-check # ruff format --check
|
|
198
|
+
make typecheck # mypy src
|
|
199
|
+
make test # pytest
|
|
200
|
+
make build # build both wheels into dist/
|
|
201
|
+
make twine-check # twine check dist/*
|
|
202
|
+
make ci # lint + format-check + typecheck + test + build + twine-check
|
|
203
|
+
make clean # remove dist/, build/, *.egg-info
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`pre-commit` is configured: `uv run pre-commit install` once, and the same
|
|
207
|
+
checks run on every commit.
|
|
208
|
+
|
|
209
|
+
## Contributing
|
|
210
|
+
|
|
211
|
+
Issues and PRs welcome. Please run the full check suite above before
|
|
212
|
+
opening a PR.
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
MIT — see [LICENSE](LICENSE).
|
|
217
|
+
|
|
218
|
+
The bundled emoji metadata derives from the work of
|
|
219
|
+
[Chalda Pnuzig](https://github.com/chalda-pnuzig/emojis.json) (ISC License,
|
|
220
|
+
see `third_party/`).
|
pymojis-1.0.1/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# pymojis
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/pymojis/)
|
|
4
|
+
[](https://pypi.org/project/pymojis/)
|
|
5
|
+
[](https://github.com/pallandir/pymojis/blob/main/LICENSE)
|
|
6
|
+
[](https://github.com/pallandir/pymojis/actions/workflows/github_ci.yaml)
|
|
7
|
+
|
|
8
|
+
A small, type-safe Python library for working with emojis: search them by
|
|
9
|
+
name / code / character, transform text, detect emojis in strings, and
|
|
10
|
+
convert to HTML.
|
|
11
|
+
|
|
12
|
+
- **Zero runtime dependencies.**
|
|
13
|
+
- **Two install sizes**: a tiny default (~230 KB of data) or the full
|
|
14
|
+
Unicode dataset (~1 MB) via the `[full]` extra.
|
|
15
|
+
- **Strict typing** — `py.typed`, full mypy strict compliance.
|
|
16
|
+
- **Fail-fast API** — bad input raises immediately, no silent `None`
|
|
17
|
+
returns or warnings.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install pymojis # lightweight: ~1900 emojis
|
|
23
|
+
pip install 'pymojis[full]' # full Unicode coverage: ~3790 emojis
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Requires **Python 3.12+**.
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from pymojis import PymojisManager
|
|
32
|
+
|
|
33
|
+
manager = PymojisManager()
|
|
34
|
+
|
|
35
|
+
# Pick random emojis
|
|
36
|
+
print([e.emoji for e in manager.get_random(length=3)])
|
|
37
|
+
# → ['😊', '🎉', '🌟']
|
|
38
|
+
|
|
39
|
+
# Look up by name / code / character
|
|
40
|
+
manager.get_by_name("grinning face with smiling eyes") # → '😄'
|
|
41
|
+
manager.get_by_code("1F604") # → '😄'
|
|
42
|
+
manager.get_by_emoji("😊") # → Emoji(...)
|
|
43
|
+
|
|
44
|
+
# Replace whole-word matches in text
|
|
45
|
+
manager.emojifie("I'm sleepy")
|
|
46
|
+
# → "I'm 😪"
|
|
47
|
+
|
|
48
|
+
# Detection
|
|
49
|
+
manager.contains_emojis("hello 👋") # → True
|
|
50
|
+
manager.is_emoji("😄") # → True
|
|
51
|
+
manager.is_emoji("😄😊") # → False
|
|
52
|
+
|
|
53
|
+
# HTML hex references
|
|
54
|
+
manager.to_html("😵💫")
|
|
55
|
+
# → "😵‍💫"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
To use the full Unicode dataset:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
manager = PymojisManager(use_full_dataset=True)
|
|
62
|
+
# Requires `pip install 'pymojis[full]'`. Raises DatasetNotFoundError
|
|
63
|
+
# with installation instructions otherwise.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API
|
|
67
|
+
|
|
68
|
+
| Method | Returns | Notes |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `get_random(categories=None, length=1, exclude=None)` | `list[Emoji]` | `categories` takes precedence over `exclude`. |
|
|
71
|
+
| `get_all_emojis(exclude=None)` | `list[Emoji]` | `exclude` accepts `"complex"` or a list of categories. |
|
|
72
|
+
| `get_by_code(code)` | `str \| None` | Single-codepoint lookup. Case-insensitive. |
|
|
73
|
+
| `get_by_name(name)` | `str \| None` | Full-name lookup. Case-insensitive. |
|
|
74
|
+
| `get_by_category(category)` | `list[str]` | All emojis in a category. |
|
|
75
|
+
| `get_by_emoji(emoji)` | `Emoji \| None` | Reverse lookup from character to record. |
|
|
76
|
+
| `contains_emojis(text)` | `bool` | True if `text` contains at least one known emoji. |
|
|
77
|
+
| `is_emoji(text)` | `bool` | True if `text.strip()` is a single known emoji. |
|
|
78
|
+
| `emojifie(text)` | `str` | Replace whole words with emojis (whose name *contains* that word). |
|
|
79
|
+
| `to_html(emoji)` | `str` | Encode each codepoint as `&#xHEX;`. |
|
|
80
|
+
|
|
81
|
+
All methods raise `TypeError` on non-`str` arguments — no silent `None`.
|
|
82
|
+
|
|
83
|
+
### Categories
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from pymojis import Categories
|
|
87
|
+
|
|
88
|
+
Categories # type alias of all valid categories:
|
|
89
|
+
# "Smileys & Emotion", "People & Body", "Animals & Nature",
|
|
90
|
+
# "Food & Drink", "Activities", "Travel & Places", "Objects",
|
|
91
|
+
# "Symbols", "Flags", "Component"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `Emoji` data model
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from pymojis import Emoji
|
|
98
|
+
|
|
99
|
+
@dataclass-like
|
|
100
|
+
class Emoji:
|
|
101
|
+
id: str # auto-generated UUID
|
|
102
|
+
emoji: str # the character itself, e.g. "😄"
|
|
103
|
+
name: str # e.g. "grinning face with smiling eyes"
|
|
104
|
+
code: list[str] # one or more Unicode codepoints, e.g. ["1F604"]
|
|
105
|
+
# (multi-codepoint emojis like ZWJ sequences have len > 1)
|
|
106
|
+
category: str # e.g. "Smileys & Emotion"
|
|
107
|
+
sub_category: str # e.g. "face-smiling"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Notes on `emojifie`
|
|
111
|
+
|
|
112
|
+
`emojifie` does whole-word matching. Each word in the input is looked up in
|
|
113
|
+
an index of emoji-name tokens. Tokens shorter than 3 characters are skipped
|
|
114
|
+
(otherwise pronouns like "I" and "m" would be replaced by ℹ and Ⓜ). When a
|
|
115
|
+
word matches multiple emojis, the first one (in dataset order) wins.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
manager.emojifie("I'm sleepy") # → "I'm 😪" (sleepy → "sleepy face")
|
|
119
|
+
manager.emojifie("zzzz xyzzy") # → "zzzz xyzzy" (no match, unchanged)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Releases (maintainer notes)
|
|
123
|
+
|
|
124
|
+
This repo publishes **two** packages on every git tag: `pymojis` and
|
|
125
|
+
`pymojis-fulldata`. Both must share the same version.
|
|
126
|
+
|
|
127
|
+
To cut a release:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# 1. Bump version in BOTH pyproject files
|
|
131
|
+
$EDITOR pyproject.toml # version = "X.Y.Z"
|
|
132
|
+
$EDITOR packages/pymojis-fulldata/pyproject.toml # version = "X.Y.Z"
|
|
133
|
+
# Also bump the [full] extra dependency pin in pyproject.toml.
|
|
134
|
+
|
|
135
|
+
# 2. Commit, tag, push
|
|
136
|
+
git commit -am "Release vX.Y.Z"
|
|
137
|
+
git tag vX.Y.Z
|
|
138
|
+
git push origin main --tags
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
CI runs the full `make ci` pipeline (lint, format, typecheck, tests,
|
|
142
|
+
build both wheels, twine check) and publishes via PyPI Trusted Publishing
|
|
143
|
+
(OIDC — no API tokens). One-time setup: register both projects as
|
|
144
|
+
Trusted Publishers on https://pypi.org pointing at this repo and the
|
|
145
|
+
workflow `.github/workflows/github_ci.yaml`.
|
|
146
|
+
|
|
147
|
+
## Development
|
|
148
|
+
|
|
149
|
+
A `Makefile` wraps the common workflows:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
make install # uv sync --extra dev --frozen
|
|
153
|
+
make lint # ruff check
|
|
154
|
+
make format-check # ruff format --check
|
|
155
|
+
make typecheck # mypy src
|
|
156
|
+
make test # pytest
|
|
157
|
+
make build # build both wheels into dist/
|
|
158
|
+
make twine-check # twine check dist/*
|
|
159
|
+
make ci # lint + format-check + typecheck + test + build + twine-check
|
|
160
|
+
make clean # remove dist/, build/, *.egg-info
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`pre-commit` is configured: `uv run pre-commit install` once, and the same
|
|
164
|
+
checks run on every commit.
|
|
165
|
+
|
|
166
|
+
## Contributing
|
|
167
|
+
|
|
168
|
+
Issues and PRs welcome. Please run the full check suite above before
|
|
169
|
+
opening a PR.
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT — see [LICENSE](LICENSE).
|
|
174
|
+
|
|
175
|
+
The bundled emoji metadata derives from the work of
|
|
176
|
+
[Chalda Pnuzig](https://github.com/chalda-pnuzig/emojis.json) (ISC License,
|
|
177
|
+
see `third_party/`).
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pymojis"
|
|
7
|
+
version = "1.0.1"
|
|
8
|
+
description = "A lightweight, type-safe Python library for emoji search, transformation, and detection."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{name = "Pallandir", email = "pallandir2@gmail.com"}
|
|
15
|
+
]
|
|
16
|
+
maintainers = [
|
|
17
|
+
{name = "Pallandir", email = "pallandir2@gmail.com"}
|
|
18
|
+
]
|
|
19
|
+
keywords = ["emoji", "unicode", "text", "utilities"]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 5 - Production/Stable",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
"Topic :: Text Processing",
|
|
29
|
+
"Topic :: Utilities",
|
|
30
|
+
"Typing :: Typed",
|
|
31
|
+
"Natural Language :: English",
|
|
32
|
+
]
|
|
33
|
+
dependencies = []
|
|
34
|
+
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
full = ["pymojis-fulldata==1.0.1"]
|
|
37
|
+
dev = [
|
|
38
|
+
"build~=1.2",
|
|
39
|
+
"pre-commit~=4.2",
|
|
40
|
+
"pytest~=8.4",
|
|
41
|
+
"pytest-cov~=6.0",
|
|
42
|
+
"pytest-mock~=3.10",
|
|
43
|
+
"twine~=6.1",
|
|
44
|
+
"mypy~=1.0",
|
|
45
|
+
"ruff~=0.8",
|
|
46
|
+
]
|
|
47
|
+
test = [
|
|
48
|
+
"pytest~=8.4",
|
|
49
|
+
"pytest-cov~=6.0",
|
|
50
|
+
"pytest-mock~=3.10",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[project.urls]
|
|
54
|
+
Homepage = "https://github.com/pallandir/pymojis"
|
|
55
|
+
Repository = "https://github.com/pallandir/pymojis"
|
|
56
|
+
Issues = "https://github.com/pallandir/pymojis/issues"
|
|
57
|
+
Changelog = "https://github.com/pallandir/pymojis/releases"
|
|
58
|
+
|
|
59
|
+
[tool.setuptools.packages.find]
|
|
60
|
+
where = ["src"]
|
|
61
|
+
include = ["pymojis*"]
|
|
62
|
+
exclude = ["tests*"]
|
|
63
|
+
|
|
64
|
+
[tool.setuptools.package-data]
|
|
65
|
+
pymojis = ["py.typed", "infrastructure/data/*.json"]
|
|
66
|
+
|
|
67
|
+
[tool.uv.workspace]
|
|
68
|
+
members = ["packages/*"]
|
|
69
|
+
|
|
70
|
+
[tool.uv.sources]
|
|
71
|
+
pymojis-fulldata = { workspace = true }
|
|
72
|
+
|
|
73
|
+
[tool.pytest.ini_options]
|
|
74
|
+
pythonpath = ["src"]
|
|
75
|
+
testpaths = ["tests"]
|
|
76
|
+
python_files = ["test_*.py", "*_test.py"]
|
|
77
|
+
python_classes = ["Test*"]
|
|
78
|
+
python_functions = ["test_*"]
|
|
79
|
+
addopts = [
|
|
80
|
+
"--strict-markers",
|
|
81
|
+
"--strict-config",
|
|
82
|
+
"-v",
|
|
83
|
+
]
|
|
84
|
+
markers = [
|
|
85
|
+
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
|
|
86
|
+
"integration: marks tests as integration tests",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[tool.ruff]
|
|
90
|
+
target-version = "py312"
|
|
91
|
+
line-length = 88
|
|
92
|
+
|
|
93
|
+
[tool.ruff.lint]
|
|
94
|
+
select = [
|
|
95
|
+
"E", # pycodestyle errors
|
|
96
|
+
"W", # pycodestyle warnings
|
|
97
|
+
"F", # pyflakes
|
|
98
|
+
"I", # isort
|
|
99
|
+
"B", # flake8-bugbear
|
|
100
|
+
"C4", # flake8-comprehensions
|
|
101
|
+
"UP", # pyupgrade
|
|
102
|
+
"T20", # print found
|
|
103
|
+
"N", # pep8-naming
|
|
104
|
+
"S", # bandit security checks
|
|
105
|
+
]
|
|
106
|
+
ignore = [
|
|
107
|
+
"E501", # line too long (handled by formatter)
|
|
108
|
+
"B008", # function calls in argument defaults
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
[tool.ruff.lint.per-file-ignores]
|
|
112
|
+
"tests/*" = ["S101"] # allow asserts in tests
|
|
113
|
+
"scripts/*" = ["T20"] # CI scripts may print
|
|
114
|
+
|
|
115
|
+
[tool.mypy]
|
|
116
|
+
python_version = "3.12"
|
|
117
|
+
strict = true
|
|
118
|
+
warn_unreachable = true
|
|
119
|
+
|
|
120
|
+
[[tool.mypy.overrides]]
|
|
121
|
+
module = "tests.*"
|
|
122
|
+
disallow_untyped_defs = false
|
|
123
|
+
|
|
124
|
+
[tool.coverage.run]
|
|
125
|
+
source = ["src"]
|
|
126
|
+
branch = true
|
|
127
|
+
omit = [
|
|
128
|
+
"tests/*",
|
|
129
|
+
"*/tests/*",
|
|
130
|
+
"*/test_*",
|
|
131
|
+
"*/conftest.py",
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
[tool.coverage.report]
|
|
135
|
+
precision = 2
|
|
136
|
+
show_missing = true
|
|
137
|
+
skip_covered = false
|
|
138
|
+
exclude_lines = [
|
|
139
|
+
"pragma: no cover",
|
|
140
|
+
"def __repr__",
|
|
141
|
+
"raise AssertionError",
|
|
142
|
+
"raise NotImplementedError",
|
|
143
|
+
"if __name__ == .__main__.:",
|
|
144
|
+
"if TYPE_CHECKING:",
|
|
145
|
+
"if typing.TYPE_CHECKING:",
|
|
146
|
+
"@overload",
|
|
147
|
+
]
|
|
148
|
+
fail_under = 80
|
pymojis-1.0.1/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pymojis.domain.entities.emojis import Categories, Emoji
|
|
4
|
+
from pymojis.infrastructure.pymojis_repository import PymojisRepositoryImpl
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PymojisManager:
|
|
8
|
+
"""High-level facade for emoji search, transformation, and detection.
|
|
9
|
+
|
|
10
|
+
By default the manager loads the lightweight bundled dataset (~228 KB).
|
|
11
|
+
Pass ``use_full_dataset=True`` to load the full dataset, which requires
|
|
12
|
+
the optional ``pymojis-fulldata`` package (``pip install 'pymojis[full]'``).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, *, use_full_dataset: bool = False) -> None:
|
|
16
|
+
self.repository = PymojisRepositoryImpl()
|
|
17
|
+
self.repository.load_emojis(kind="full" if use_full_dataset else "light")
|
|
18
|
+
|
|
19
|
+
def get_random(
|
|
20
|
+
self,
|
|
21
|
+
categories: list[Categories] | None = None,
|
|
22
|
+
length: int = 1,
|
|
23
|
+
exclude: Literal["complex"] | list[Categories] | None = None,
|
|
24
|
+
) -> list[Emoji]:
|
|
25
|
+
"""Return ``length`` random ``Emoji`` objects, optionally filtered.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
categories: If provided, only emojis from these categories are
|
|
29
|
+
considered. Takes precedence over ``exclude``.
|
|
30
|
+
length: Number of emojis to return. Capped at the size of the pool.
|
|
31
|
+
exclude: ``"complex"`` to exclude multi-codepoint emojis (skin
|
|
32
|
+
tones, ZWJ sequences), or a list of categories to skip.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
>>> manager = PymojisManager()
|
|
36
|
+
>>> [e.emoji for e in manager.get_random(length=2)] # doctest: +SKIP
|
|
37
|
+
['😊', '🌟']
|
|
38
|
+
"""
|
|
39
|
+
return self.repository.get_random_emojis(categories, length, exclude)
|
|
40
|
+
|
|
41
|
+
def get_all_emojis(
|
|
42
|
+
self, exclude: Literal["complex"] | list[Categories] | None = None
|
|
43
|
+
) -> list[Emoji]:
|
|
44
|
+
"""Return every loaded ``Emoji``, optionally filtered by ``exclude``."""
|
|
45
|
+
return self.repository.get_all(exclude)
|
|
46
|
+
|
|
47
|
+
def get_by_code(self, code: str) -> str | None:
|
|
48
|
+
"""Return the emoji character matching the given Unicode codepoint.
|
|
49
|
+
|
|
50
|
+
Only single-codepoint emojis are addressable this way. Returns
|
|
51
|
+
``None`` if no match.
|
|
52
|
+
"""
|
|
53
|
+
return self.repository.get_by_code(code)
|
|
54
|
+
|
|
55
|
+
def get_by_name(self, name: str) -> str | None:
|
|
56
|
+
"""Return the emoji character whose full name equals ``name``.
|
|
57
|
+
|
|
58
|
+
Matching is case-insensitive. Returns ``None`` if no match.
|
|
59
|
+
"""
|
|
60
|
+
return self.repository.get_by_name(name)
|
|
61
|
+
|
|
62
|
+
def get_by_category(self, category: Categories) -> list[str]:
|
|
63
|
+
"""Return all emoji characters in the given category."""
|
|
64
|
+
return self.repository.get_by_category(category)
|
|
65
|
+
|
|
66
|
+
def get_by_emoji(self, emoji: str) -> Emoji | None:
|
|
67
|
+
"""Return the ``Emoji`` object whose character equals ``emoji``."""
|
|
68
|
+
return self.repository.get_by_emoji(emoji)
|
|
69
|
+
|
|
70
|
+
def contains_emojis(self, text: str) -> bool:
|
|
71
|
+
"""Return ``True`` if ``text`` contains at least one known emoji."""
|
|
72
|
+
return self.repository.contains_emojis(text)
|
|
73
|
+
|
|
74
|
+
def is_emoji(self, text: str) -> bool:
|
|
75
|
+
"""Return ``True`` if ``text`` (after stripping) is a single known emoji."""
|
|
76
|
+
return self.repository.is_emoji(text)
|
|
77
|
+
|
|
78
|
+
def emojifie(self, text: str) -> str:
|
|
79
|
+
"""Replace each whole word in ``text`` whose lowercase form is a token
|
|
80
|
+
of an emoji name with that emoji.
|
|
81
|
+
|
|
82
|
+
Tokens are matched against the first word of any emoji's name (e.g.
|
|
83
|
+
``"sleepy"`` matches the emoji named ``"sleepy face"``). Words that
|
|
84
|
+
match no emoji are left unchanged.
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
>>> manager = PymojisManager()
|
|
88
|
+
>>> manager.emojifie("I'm sleepy") # doctest: +SKIP
|
|
89
|
+
"I'm 😪"
|
|
90
|
+
"""
|
|
91
|
+
return self.repository.emojifie(text)
|
|
92
|
+
|
|
93
|
+
def to_html(self, emoji: str) -> str:
|
|
94
|
+
"""Return ``emoji`` as a sequence of HTML hex character references.
|
|
95
|
+
|
|
96
|
+
Multi-codepoint emojis (ZWJ sequences, variation selectors) are
|
|
97
|
+
encoded codepoint by codepoint.
|
|
98
|
+
|
|
99
|
+
Example:
|
|
100
|
+
>>> PymojisManager().to_html("😪") # doctest: +SKIP
|
|
101
|
+
'😪'
|
|
102
|
+
"""
|
|
103
|
+
return self.repository.to_html(emoji)
|
|
File without changes
|
|
File without changes
|