randino 1.0.0__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.
Files changed (47) hide show
  1. randino-1.0.0/.gitignore +40 -0
  2. randino-1.0.0/CHANGELOG.md +11 -0
  3. randino-1.0.0/LICENSE +21 -0
  4. randino-1.0.0/PKG-INFO +181 -0
  5. randino-1.0.0/README.md +149 -0
  6. randino-1.0.0/pyproject.toml +115 -0
  7. randino-1.0.0/src/randino/__init__.py +84 -0
  8. randino-1.0.0/src/randino/_internal/__init__.py +1 -0
  9. randino-1.0.0/src/randino/_internal/parse.py +50 -0
  10. randino-1.0.0/src/randino/_internal/utils.py +63 -0
  11. randino-1.0.0/src/randino/_types.py +107 -0
  12. randino-1.0.0/src/randino/name/__init__.py +25 -0
  13. randino-1.0.0/src/randino/name/_generator.py +597 -0
  14. randino-1.0.0/src/randino/name/_romanize.py +334 -0
  15. randino-1.0.0/src/randino/name/data/__init__.py +45 -0
  16. randino-1.0.0/src/randino/name/data/_types.py +133 -0
  17. randino-1.0.0/src/randino/name/data/de.py +34 -0
  18. randino-1.0.0/src/randino/name/data/en.py +63 -0
  19. randino-1.0.0/src/randino/name/data/es.py +35 -0
  20. randino-1.0.0/src/randino/name/data/it.py +35 -0
  21. randino-1.0.0/src/randino/name/data/ja.py +52 -0
  22. randino-1.0.0/src/randino/name/data/ko.py +92 -0
  23. randino-1.0.0/src/randino/name/data/ru.py +46 -0
  24. randino-1.0.0/src/randino/name/data/syllables.py +57 -0
  25. randino-1.0.0/src/randino/name/data/vi.py +41 -0
  26. randino-1.0.0/src/randino/name/data/zh.py +64 -0
  27. randino-1.0.0/src/randino/name/name_length_range.py +51 -0
  28. randino-1.0.0/src/randino/name/name_supports_middle_name.py +24 -0
  29. randino-1.0.0/src/randino/name/name_supports_roman.py +18 -0
  30. randino-1.0.0/src/randino/name/random_name.py +72 -0
  31. randino-1.0.0/src/randino/name/random_name_details.py +42 -0
  32. randino-1.0.0/src/randino/nickname/__init__.py +27 -0
  33. randino-1.0.0/src/randino/nickname/_generator.py +614 -0
  34. randino-1.0.0/src/randino/nickname/data/__init__.py +56 -0
  35. randino-1.0.0/src/randino/nickname/data/_types.py +93 -0
  36. randino-1.0.0/src/randino/nickname/data/en.py +172 -0
  37. randino-1.0.0/src/randino/nickname/data/ja.py +138 -0
  38. randino-1.0.0/src/randino/nickname/data/ko.py +153 -0
  39. randino-1.0.0/src/randino/nickname/data/zh.py +134 -0
  40. randino-1.0.0/src/randino/nickname/nickname_length_range.py +42 -0
  41. randino-1.0.0/src/randino/nickname/random_nickname.py +93 -0
  42. randino-1.0.0/src/randino/nickname/random_nickname_details.py +51 -0
  43. randino-1.0.0/src/randino/py.typed +0 -0
  44. randino-1.0.0/tests/__init__.py +1 -0
  45. randino-1.0.0/tests/test_base.py +101 -0
  46. randino-1.0.0/tests/test_name.py +377 -0
  47. randino-1.0.0/tests/test_nickname.py +402 -0
@@ -0,0 +1,40 @@
1
+ # Build output
2
+ dist/
3
+ docs-dist/
4
+
5
+ # Node.js
6
+ node_modules/
7
+
8
+ # Dart
9
+ .dart_tool/
10
+ build/
11
+ # A library pins nothing, so its lock file is noise in every diff.
12
+ packages/dart/pubspec.lock
13
+
14
+ # Python
15
+ __pycache__/
16
+ *.py[cod]
17
+ .venv/
18
+ *.egg-info/
19
+ .pytest_cache/
20
+ .mypy_cache/
21
+ .ruff_cache/
22
+
23
+ # VitePress
24
+ docs/.vitepress/.temp/
25
+ docs/.vitepress/cache/
26
+
27
+ # Generated by `docs/scripts/copy-changelog.mjs` from each package's CHANGELOG.md
28
+ docs/*/changelog.md
29
+
30
+ # IDEs
31
+ .idea/
32
+
33
+ # Generated by MacOS
34
+ .DS_Store
35
+
36
+ # Generated by Windows
37
+ Thumbs.db
38
+
39
+ # Log files
40
+ *.log
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ **2026-09-01**
6
+
7
+ - The first release of the Python package, ported from the JavaScript one.
8
+ - `random_name` and `random_name_details` generate person names in 9 languages, with the English pronunciation of each.
9
+ - `random_nickname` and `random_nickname_details` generate nicknames in 4 languages across 14 themes.
10
+ - `name_length_range`, `name_supports_middle_name`, `name_supports_roman` and `nickname_length_range` report what a language can produce before you ask it to.
11
+ - No dependencies. Requires Python 3.10 or newer, and ships a `py.typed` marker.
randino-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CDGet <jooy2.contact@gmail.com> (https://cdget.com).
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
randino-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,181 @@
1
+ Metadata-Version: 2.5
2
+ Name: randino
3
+ Version: 1.0.0
4
+ Summary: Generates random person names and nicknames in the language you ask for — Korean, English, Japanese, Chinese and six more. No dependencies, pure Python.
5
+ Project-URL: Homepage, https://randino.cdget.com
6
+ Project-URL: Documentation, https://randino.cdget.com
7
+ Project-URL: Repository, https://github.com/jooy2/randino
8
+ Project-URL: Issues, https://github.com/jooy2/randino/issues
9
+ Project-URL: Changelog, https://randino.cdget.com/changelog
10
+ Author-email: CDGet <jooy2.contact@gmail.com>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: dummy,i18n,mock,name,name-generator,nickname,nickname-generator,random,romanize,username
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Software Development :: Testing
24
+ Classifier: Topic :: Text Processing :: Linguistic
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.10
27
+ Provides-Extra: dev
28
+ Requires-Dist: mypy>=1.15; extra == 'dev'
29
+ Requires-Dist: pytest>=8.3; extra == 'dev'
30
+ Requires-Dist: ruff>=0.9; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ <img src="https://raw.githubusercontent.com/jooy2/randino/main/docs/public/128x128.png" alt="randino" width="96" height="96" />
34
+
35
+ # randino for Python
36
+
37
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jooy2/randino/blob/main/LICENSE) [![pypi package](https://img.shields.io/pypi/v/randino.svg)](https://pypi.org/project/randino/)
38
+
39
+ ### 📘 [**randino.cdget.com**](https://randino.cdget.com)
40
+
41
+ Every option and every example, with **Python** picked in the sidebar. This README is just the quick start.
42
+
43
+ ---
44
+
45
+ **randino** generates random person names and nicknames in the language you ask for.
46
+
47
+ - **Person names** read like names people actually carry — 김민준, Emma Clover, Иванов Иван — and come with their English pronunciation. 9 languages.
48
+ - **Nicknames** are the handles you would pick for a game or a website — 멋진사자, MistyOwl, 고양이꼬리. Built from everyday words across fourteen themes, never from person names.
49
+ - Every argument is keyword-only and optional, so `random_name()` on its own works.
50
+ - **Pure Python, no dependencies.** It imports nothing outside the standard library, and ships a `py.typed` marker so mypy and Pyright read the annotations.
51
+
52
+ This is the Python half. The [npm package](https://www.npmjs.com/package/randino) and the [pub.dev package](https://pub.dev/packages/randino) are the others, and all three generate from the same datasets under the same rules. They version independently, so this package's number and the others' will not always agree.
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ pip install randino
58
+ ```
59
+
60
+ Requires **Python 3.10 or newer**. There is nothing else to install.
61
+
62
+ ## Person names
63
+
64
+ ```python
65
+ from randino import random_name, random_name_details
66
+
67
+ random_name()
68
+ # ['Emma Clover']
69
+
70
+ random_name(language="ko", count=3)
71
+ # ['김태윤', '원동혁', '조진우']
72
+
73
+ random_name(language="ko", script="roman")
74
+ # ['Kim Minjun']
75
+
76
+ random_name(language="en", gender="female", include_middle_name=True)
77
+ # ['Grace Amelia Bennett']
78
+
79
+ random_name_details(language="ko")[0]
80
+ # NameDetail(native='여미주', roman='Yeo Miju', language='ko', gender='female')
81
+ ```
82
+
83
+ | Argument | Type | Default |
84
+ | --------------------------- | ----------------------------- | ---------- |
85
+ | `language` | `NameLanguageOption` | `"all"` |
86
+ | `gender` | `NameGenderOption` | `"all"` |
87
+ | `count` | `int` | `1` |
88
+ | `style` | `int` (0 real … 100 invented) | `0` |
89
+ | `min_length` / `max_length` | `int \| None` | _language_ |
90
+ | `include_surname` | `bool` | `True` |
91
+ | `include_middle_name` | `bool` | `False` |
92
+ | `script` | `NameScript` | `"native"` |
93
+ | `starts_with` | `str` | `""` |
94
+ | `unique` | `bool` | `False` |
95
+
96
+ `random_name_details` takes the same arguments except `script`, and returns a `NameDetail` — `native`, `roman`, `language` and `gender` — for each name.
97
+
98
+ ## Nicknames
99
+
100
+ ```python
101
+ from randino import random_nickname, random_nickname_details
102
+
103
+ random_nickname(language="ko", count=3)
104
+ # ['오래된곰', '영원한도마뱀', '귀여운신화다발']
105
+
106
+ random_nickname(language="en", count=2)
107
+ # ['FoggyHillside', 'CraneVoyage']
108
+
109
+ random_nickname(language="ko", theme="animal", count=2)
110
+ # ['깊은연어', '하얀여우갈기']
111
+
112
+ random_nickname(language="ko", unique_suffix=True, count=2)
113
+ # ['달력_U7aNZ', '조용한바구니_RUKAP']
114
+
115
+ random_nickname(base_word="고양이", count=3)
116
+ # ['하얀고양이', '고양이바람', '귀여운고양이뿔']
117
+
118
+ random_nickname_details(language="ko", unique_suffix=True)[0]
119
+ # NicknameDetail(nickname='오래된발견_zVShs', words=('오래된', '발견'), suffix='_zVShs', language='ko', theme='concept')
120
+ ```
121
+
122
+ | Argument | Type | Default |
123
+ | --------------------------- | --------------------------------- | ---------- |
124
+ | `language` | `NicknameLanguageOption \| None` | `None` |
125
+ | `theme` | `NicknameThemeOption` | `"all"` |
126
+ | `count` | `int` | `1` |
127
+ | `style` | `int` (0 real … 100 invented) | `0` |
128
+ | `min_length` / `max_length` | `int \| None` | _language_ |
129
+ | `include_modifier` | `bool` | `True` |
130
+ | `word_separator` | `str \| None` | _language_ |
131
+ | `base_word` | `str` | `""` |
132
+ | `unique_suffix` | `bool` | `False` |
133
+ | `unique_suffix_length` | `int` | `5` |
134
+ | `unique_suffix_separator` | `str` | `"_"` |
135
+ | `unique_suffix_charset` | `str` | _built-in_ |
136
+ | `starts_with` | `str` | `""` |
137
+ | `unique` | `bool` | `False` |
138
+
139
+ `language` is the one argument whose default is `None` rather than `"all"`, and the two are not the same thing: left out, a `base_word` picks the language it is written in, so `"고양이"` is never handed an English modifier. Passing `"all"` mixes every language regardless.
140
+
141
+ Themes: `animal`, `object`, `nature`, `plant`, `gem`, `concept`, `myth`, `job`, `music`, `place`, `food`, `sport`, `vehicle`, `product`.
142
+
143
+ ## Helpers and constants
144
+
145
+ ```python
146
+ from randino import name_length_range, name_supports_roman, nickname_length_range
147
+
148
+ name_length_range("ko") # (3, 3)
149
+ name_length_range("en", include_middle_name=True) # (12, 24)
150
+ name_supports_middle_name("ko") # False
151
+ name_supports_roman("en") # False
152
+ nickname_length_range("ko") # (1, 12)
153
+ ```
154
+
155
+ `NAME_LANGUAGES`, `NICKNAME_LANGUAGES` and `NICKNAME_THEMES` list what the generators accept; `NAME_COUNT_MAX`, `NAME_LENGTH_MIN` / `MAX`, `NICKNAME_COUNT_MAX`, `NICKNAME_LENGTH_MIN` / `MAX`, `NICKNAME_SUFFIX_LENGTH_MAX` and `NICKNAME_SUFFIX_CHARSET` are the bounds every argument is clamped to.
156
+
157
+ ## Differences from the npm package
158
+
159
+ The two generate the same output from the same data, and only the surface is Python's rather than JavaScript's.
160
+
161
+ | npm | PyPI |
162
+ | ------------------------------------------ | ----------------------------------------------- |
163
+ | One options object | Keyword-only arguments |
164
+ | `includeSurname`, `minLength` | `include_surname`, `min_length` |
165
+ | `language: 'ko'`, `language: 'all'` | The same strings, as `Literal` types |
166
+ | `[number, number]` | `tuple[int, int]` |
167
+ | `NameDetail` / `NicknameDetail` interfaces | The same two names, as frozen dataclasses |
168
+ | `detail.words` is an array | `detail.words` is a tuple |
169
+
170
+ ## Development
171
+
172
+ ```bash
173
+ uv venv && uv pip install -e ".[dev]"
174
+ pytest
175
+ ruff check . && ruff format --check .
176
+ mypy
177
+ ```
178
+
179
+ ## License
180
+
181
+ MIT © [CDGet](https://cdget.com)
@@ -0,0 +1,149 @@
1
+ <img src="https://raw.githubusercontent.com/jooy2/randino/main/docs/public/128x128.png" alt="randino" width="96" height="96" />
2
+
3
+ # randino for Python
4
+
5
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jooy2/randino/blob/main/LICENSE) [![pypi package](https://img.shields.io/pypi/v/randino.svg)](https://pypi.org/project/randino/)
6
+
7
+ ### 📘 [**randino.cdget.com**](https://randino.cdget.com)
8
+
9
+ Every option and every example, with **Python** picked in the sidebar. This README is just the quick start.
10
+
11
+ ---
12
+
13
+ **randino** generates random person names and nicknames in the language you ask for.
14
+
15
+ - **Person names** read like names people actually carry — 김민준, Emma Clover, Иванов Иван — and come with their English pronunciation. 9 languages.
16
+ - **Nicknames** are the handles you would pick for a game or a website — 멋진사자, MistyOwl, 고양이꼬리. Built from everyday words across fourteen themes, never from person names.
17
+ - Every argument is keyword-only and optional, so `random_name()` on its own works.
18
+ - **Pure Python, no dependencies.** It imports nothing outside the standard library, and ships a `py.typed` marker so mypy and Pyright read the annotations.
19
+
20
+ This is the Python half. The [npm package](https://www.npmjs.com/package/randino) and the [pub.dev package](https://pub.dev/packages/randino) are the others, and all three generate from the same datasets under the same rules. They version independently, so this package's number and the others' will not always agree.
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ pip install randino
26
+ ```
27
+
28
+ Requires **Python 3.10 or newer**. There is nothing else to install.
29
+
30
+ ## Person names
31
+
32
+ ```python
33
+ from randino import random_name, random_name_details
34
+
35
+ random_name()
36
+ # ['Emma Clover']
37
+
38
+ random_name(language="ko", count=3)
39
+ # ['김태윤', '원동혁', '조진우']
40
+
41
+ random_name(language="ko", script="roman")
42
+ # ['Kim Minjun']
43
+
44
+ random_name(language="en", gender="female", include_middle_name=True)
45
+ # ['Grace Amelia Bennett']
46
+
47
+ random_name_details(language="ko")[0]
48
+ # NameDetail(native='여미주', roman='Yeo Miju', language='ko', gender='female')
49
+ ```
50
+
51
+ | Argument | Type | Default |
52
+ | --------------------------- | ----------------------------- | ---------- |
53
+ | `language` | `NameLanguageOption` | `"all"` |
54
+ | `gender` | `NameGenderOption` | `"all"` |
55
+ | `count` | `int` | `1` |
56
+ | `style` | `int` (0 real … 100 invented) | `0` |
57
+ | `min_length` / `max_length` | `int \| None` | _language_ |
58
+ | `include_surname` | `bool` | `True` |
59
+ | `include_middle_name` | `bool` | `False` |
60
+ | `script` | `NameScript` | `"native"` |
61
+ | `starts_with` | `str` | `""` |
62
+ | `unique` | `bool` | `False` |
63
+
64
+ `random_name_details` takes the same arguments except `script`, and returns a `NameDetail` — `native`, `roman`, `language` and `gender` — for each name.
65
+
66
+ ## Nicknames
67
+
68
+ ```python
69
+ from randino import random_nickname, random_nickname_details
70
+
71
+ random_nickname(language="ko", count=3)
72
+ # ['오래된곰', '영원한도마뱀', '귀여운신화다발']
73
+
74
+ random_nickname(language="en", count=2)
75
+ # ['FoggyHillside', 'CraneVoyage']
76
+
77
+ random_nickname(language="ko", theme="animal", count=2)
78
+ # ['깊은연어', '하얀여우갈기']
79
+
80
+ random_nickname(language="ko", unique_suffix=True, count=2)
81
+ # ['달력_U7aNZ', '조용한바구니_RUKAP']
82
+
83
+ random_nickname(base_word="고양이", count=3)
84
+ # ['하얀고양이', '고양이바람', '귀여운고양이뿔']
85
+
86
+ random_nickname_details(language="ko", unique_suffix=True)[0]
87
+ # NicknameDetail(nickname='오래된발견_zVShs', words=('오래된', '발견'), suffix='_zVShs', language='ko', theme='concept')
88
+ ```
89
+
90
+ | Argument | Type | Default |
91
+ | --------------------------- | --------------------------------- | ---------- |
92
+ | `language` | `NicknameLanguageOption \| None` | `None` |
93
+ | `theme` | `NicknameThemeOption` | `"all"` |
94
+ | `count` | `int` | `1` |
95
+ | `style` | `int` (0 real … 100 invented) | `0` |
96
+ | `min_length` / `max_length` | `int \| None` | _language_ |
97
+ | `include_modifier` | `bool` | `True` |
98
+ | `word_separator` | `str \| None` | _language_ |
99
+ | `base_word` | `str` | `""` |
100
+ | `unique_suffix` | `bool` | `False` |
101
+ | `unique_suffix_length` | `int` | `5` |
102
+ | `unique_suffix_separator` | `str` | `"_"` |
103
+ | `unique_suffix_charset` | `str` | _built-in_ |
104
+ | `starts_with` | `str` | `""` |
105
+ | `unique` | `bool` | `False` |
106
+
107
+ `language` is the one argument whose default is `None` rather than `"all"`, and the two are not the same thing: left out, a `base_word` picks the language it is written in, so `"고양이"` is never handed an English modifier. Passing `"all"` mixes every language regardless.
108
+
109
+ Themes: `animal`, `object`, `nature`, `plant`, `gem`, `concept`, `myth`, `job`, `music`, `place`, `food`, `sport`, `vehicle`, `product`.
110
+
111
+ ## Helpers and constants
112
+
113
+ ```python
114
+ from randino import name_length_range, name_supports_roman, nickname_length_range
115
+
116
+ name_length_range("ko") # (3, 3)
117
+ name_length_range("en", include_middle_name=True) # (12, 24)
118
+ name_supports_middle_name("ko") # False
119
+ name_supports_roman("en") # False
120
+ nickname_length_range("ko") # (1, 12)
121
+ ```
122
+
123
+ `NAME_LANGUAGES`, `NICKNAME_LANGUAGES` and `NICKNAME_THEMES` list what the generators accept; `NAME_COUNT_MAX`, `NAME_LENGTH_MIN` / `MAX`, `NICKNAME_COUNT_MAX`, `NICKNAME_LENGTH_MIN` / `MAX`, `NICKNAME_SUFFIX_LENGTH_MAX` and `NICKNAME_SUFFIX_CHARSET` are the bounds every argument is clamped to.
124
+
125
+ ## Differences from the npm package
126
+
127
+ The two generate the same output from the same data, and only the surface is Python's rather than JavaScript's.
128
+
129
+ | npm | PyPI |
130
+ | ------------------------------------------ | ----------------------------------------------- |
131
+ | One options object | Keyword-only arguments |
132
+ | `includeSurname`, `minLength` | `include_surname`, `min_length` |
133
+ | `language: 'ko'`, `language: 'all'` | The same strings, as `Literal` types |
134
+ | `[number, number]` | `tuple[int, int]` |
135
+ | `NameDetail` / `NicknameDetail` interfaces | The same two names, as frozen dataclasses |
136
+ | `detail.words` is an array | `detail.words` is a tuple |
137
+
138
+ ## Development
139
+
140
+ ```bash
141
+ uv venv && uv pip install -e ".[dev]"
142
+ pytest
143
+ ruff check . && ruff format --check .
144
+ mypy
145
+ ```
146
+
147
+ ## License
148
+
149
+ MIT © [CDGet](https://cdget.com)
@@ -0,0 +1,115 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "randino"
7
+ version = "1.0.0"
8
+ description = "Generates random person names and nicknames in the language you ask for — Korean, English, Japanese, Chinese and six more. No dependencies, pure Python."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ # 3.10 is where `X | Y` in annotations and `dataclass(slots=True)` both landed,
13
+ # and 3.9 reached end of life in October 2025.
14
+ requires-python = ">=3.10"
15
+ authors = [{ name = "CDGet", email = "jooy2.contact@gmail.com" }]
16
+ keywords = [
17
+ "random",
18
+ "name",
19
+ "nickname",
20
+ "username",
21
+ "name-generator",
22
+ "nickname-generator",
23
+ "mock",
24
+ "dummy",
25
+ "i18n",
26
+ "romanize",
27
+ ]
28
+ classifiers = [
29
+ "Development Status :: 5 - Production/Stable",
30
+ "Intended Audience :: Developers",
31
+ "Operating System :: OS Independent",
32
+ "Programming Language :: Python :: 3 :: Only",
33
+ "Programming Language :: Python :: 3.10",
34
+ "Programming Language :: Python :: 3.11",
35
+ "Programming Language :: Python :: 3.12",
36
+ "Programming Language :: Python :: 3.13",
37
+ "Programming Language :: Python :: 3.14",
38
+ "Topic :: Software Development :: Testing",
39
+ "Topic :: Text Processing :: Linguistic",
40
+ "Typing :: Typed",
41
+ ]
42
+ # No runtime dependencies. This is a hard constraint, not a preference — it is
43
+ # why the Hangul romanizer is written out in `name/_romanize.py`.
44
+ dependencies = []
45
+
46
+ [project.urls]
47
+ Homepage = "https://randino.cdget.com"
48
+ Documentation = "https://randino.cdget.com"
49
+ Repository = "https://github.com/jooy2/randino"
50
+ Issues = "https://github.com/jooy2/randino/issues"
51
+ Changelog = "https://randino.cdget.com/changelog"
52
+
53
+ [project.optional-dependencies]
54
+ dev = ["mypy>=1.15", "pytest>=8.3", "ruff>=0.9"]
55
+
56
+ [tool.hatch.build.targets.wheel]
57
+ packages = ["src/randino"]
58
+
59
+ [tool.hatch.build.targets.sdist]
60
+ # The tests travel with the source distribution; nothing else outside the package
61
+ # is worth shipping.
62
+ include = ["src/randino", "tests", "README.md", "CHANGELOG.md", "LICENSE"]
63
+ # Hatchling also ships the VCS ignore file it finds walking up, which here is the
64
+ # repository's own. Neither `exclude` nor `ignore-vcs` suppresses it, so the
65
+ # sdist carries a stray `.gitignore` describing `node_modules/`. Harmless, and
66
+ # not worth dead configuration that does not actually remove it.
67
+
68
+ [tool.ruff]
69
+ line-length = 100
70
+ src = ["src", "tests"]
71
+
72
+ [tool.ruff.lint]
73
+ select = [
74
+ "E", # pycodestyle
75
+ "W", # pycodestyle warnings
76
+ "F", # pyflakes
77
+ "I", # import sorting
78
+ "N", # pep8-naming
79
+ "UP", # pyupgrade
80
+ "B", # bugbear
81
+ "SIM", # flake8-simplify
82
+ "RUF", # ruff's own
83
+ "D", # pydocstyle — everything public carries a docstring
84
+ ]
85
+ ignore = [
86
+ # The datasets are whitespace-separated pools inside a triple-quoted string.
87
+ # Their line lengths follow the JavaScript package's, and reflowing them by
88
+ # hand to a column count would make the two harder to diff.
89
+ "E501",
90
+ # A one-line summary is what these modules and functions have; a blank line
91
+ # before a closing `"""` and a mandated period are noise.
92
+ "D203",
93
+ "D213",
94
+ # Homoglyph detection, aimed at a Cyrillic `а` smuggled into an identifier.
95
+ # Half of what this package ships is Cyrillic, Hangul and kana pools, and the
96
+ # romanizer's comments quote them — every hit here is the data doing its job.
97
+ "RUF001",
98
+ "RUF002",
99
+ "RUF003",
100
+ ]
101
+
102
+ [tool.ruff.lint.per-file-ignores]
103
+ # Tests describe themselves in their names; `data/` files are pools, not prose.
104
+ "tests/*" = ["D103"]
105
+
106
+ [tool.ruff.lint.pydocstyle]
107
+ convention = "google"
108
+
109
+ [tool.ruff.format]
110
+ docstring-code-format = true
111
+
112
+ [tool.mypy]
113
+ files = ["src", "tests"]
114
+ python_version = "3.10"
115
+ strict = true
@@ -0,0 +1,84 @@
1
+ """randino — random person names and nicknames, per language, with no dependencies.
2
+
3
+ Two separate concerns, deliberately. `random_name` produces names a person could
4
+ actually carry (`김민준`, `Emma Clover`); `random_nickname` produces the handle
5
+ someone would pick for a game or a website (`멋진사자`, `MistyOwl`), built from
6
+ everyday words and never from person names.
7
+
8
+ Example:
9
+ >>> from randino import random_name, random_nickname
10
+ >>> random_name(language="ko", count=3)
11
+ ['김민준', '이서연', '박지호']
12
+ >>> random_nickname(language="en")
13
+ ['MistyOwl']
14
+ """
15
+
16
+ from randino._types import (
17
+ NameDetail,
18
+ NameGender,
19
+ NameGenderOption,
20
+ NameLanguage,
21
+ NameLanguageOption,
22
+ NameScript,
23
+ NicknameDetail,
24
+ NicknameLanguage,
25
+ NicknameLanguageOption,
26
+ NicknameTheme,
27
+ NicknameThemeOption,
28
+ )
29
+ from randino.name import (
30
+ NAME_COUNT_MAX,
31
+ NAME_LANGUAGES,
32
+ NAME_LENGTH_MAX,
33
+ NAME_LENGTH_MIN,
34
+ name_length_range,
35
+ name_supports_middle_name,
36
+ name_supports_roman,
37
+ random_name,
38
+ random_name_details,
39
+ )
40
+ from randino.nickname import (
41
+ NICKNAME_COUNT_MAX,
42
+ NICKNAME_LANGUAGES,
43
+ NICKNAME_LENGTH_MAX,
44
+ NICKNAME_LENGTH_MIN,
45
+ NICKNAME_SUFFIX_CHARSET,
46
+ NICKNAME_SUFFIX_LENGTH_MAX,
47
+ NICKNAME_THEMES,
48
+ nickname_length_range,
49
+ random_nickname,
50
+ random_nickname_details,
51
+ )
52
+
53
+ __all__ = [
54
+ "NAME_COUNT_MAX",
55
+ "NAME_LANGUAGES",
56
+ "NAME_LENGTH_MAX",
57
+ "NAME_LENGTH_MIN",
58
+ "NICKNAME_COUNT_MAX",
59
+ "NICKNAME_LANGUAGES",
60
+ "NICKNAME_LENGTH_MAX",
61
+ "NICKNAME_LENGTH_MIN",
62
+ "NICKNAME_SUFFIX_CHARSET",
63
+ "NICKNAME_SUFFIX_LENGTH_MAX",
64
+ "NICKNAME_THEMES",
65
+ "NameDetail",
66
+ "NameGender",
67
+ "NameGenderOption",
68
+ "NameLanguage",
69
+ "NameLanguageOption",
70
+ "NameScript",
71
+ "NicknameDetail",
72
+ "NicknameLanguage",
73
+ "NicknameLanguageOption",
74
+ "NicknameTheme",
75
+ "NicknameThemeOption",
76
+ "name_length_range",
77
+ "name_supports_middle_name",
78
+ "name_supports_roman",
79
+ "nickname_length_range",
80
+ "random_name",
81
+ "random_name_details",
82
+ "random_nickname",
83
+ "random_nickname_details",
84
+ ]
@@ -0,0 +1 @@
1
+ """Helpers shared by both generators. Nothing here is part of the public API."""
@@ -0,0 +1,50 @@
1
+ """Helpers that keep the datasets readable.
2
+
3
+ Pools are written as whitespace-separated strings inside a triple-quoted string
4
+ instead of one list entry per line, which keeps a 120-name pool to a handful of
5
+ lines.
6
+ """
7
+
8
+ from typing import NamedTuple
9
+
10
+
11
+ class NameToken(NamedTuple):
12
+ """A name part that carries its own romanization (Japanese kanji, Chinese hanzi)."""
13
+
14
+ n: str
15
+ """The part in its own script."""
16
+
17
+ r: str
18
+ """How that part is read in the Latin alphabet."""
19
+
20
+
21
+ def words(source: str) -> tuple[str, ...]:
22
+ """Split a whitespace-separated pool.
23
+
24
+ `_` stands for a space inside a single entry, so multi-word names survive the
25
+ split (`De_Luca` -> `De Luca`).
26
+ """
27
+ return tuple(word.replace("_", " ") for word in source.split())
28
+
29
+
30
+ def tokens(source: str) -> tuple[NameToken, ...]:
31
+ """Split a whitespace-separated pool of `native:roman` pairs.
32
+
33
+ For scripts whose characters carry their own reading (Japanese kanji, Chinese
34
+ hanzi).
35
+ """
36
+ return tuple(NameToken(*pair.split(":", 1)) for pair in words(source))
37
+
38
+
39
+ def weights(source: str) -> dict[str, int]:
40
+ """Split a whitespace-separated pool of `native:weight` pairs into a lookup.
41
+
42
+ For pools whose entries are not equally likely (surname frequency). Entries left
43
+ out of the source keep whatever default the caller falls back to.
44
+ """
45
+ return {token.n: int(token.r) for token in tokens(source)}
46
+
47
+
48
+ def roman_map(source: str) -> dict[str, str]:
49
+ """Build a native -> romanization lookup from `native:roman` pairs."""
50
+ return {token.n: token.r for token in tokens(source)}