Metadata-Version: 2.5
Name: randino
Version: 1.5.0
Summary: Generates random person names, nicknames, words, sentences, real locations, ages, genders, fictional organizations, dates and phone numbers in nine languages, and the real operating system, device, hardware, software versions, app stores and file extensions of a sample machine. No dependencies, pure Python.
Project-URL: Homepage, https://randino.cdget.com
Project-URL: Documentation, https://randino.cdget.com
Project-URL: Repository, https://github.com/jooy2/randino
Project-URL: Issues, https://github.com/jooy2/randino/issues
Project-URL: Changelog, https://randino.cdget.com/changelog
Author-email: CDGet <jooy2.contact@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: age,cpu,date,device,dummy,fake-data,file-extension,gender,gpu,hardware,i18n,location,mock,mock-data,name,name-generator,nickname,nickname-generator,organization,os,phone,random,romanize,sentence,sentence-generator,system,test-data,username,version,word,word-generator
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Text Processing :: Linguistic
Classifier: Typing :: Typed
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: mypy>=1.15; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.9; extra == 'dev'
Description-Content-Type: text/markdown

<img src="https://raw.githubusercontent.com/jooy2/randino/main/docs/public/128x128.png" alt="randino" width="96" height="96" />

# randino for Python

[![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/)

### 📘 [**randino.cdget.com**](https://randino.cdget.com)

The full documentation, with **Python** picked in the sidebar. This README is the quick start.

---

**randino** generates random sample data in nine languages: person names, nicknames, words, sentences, real places, ages, genders, organizations, dates and phone numbers, and the operating system, hardware and files of a sample machine.

- **Written the way each language writes it.** Names, nicknames and sentences follow the language's own script, word order and grammar: 김민준, Emma Clover, 여우가 사과를 먹는다.
- **Real only where it should be.** Places and system values are real, such as 서울특별시 종로구 and Apple iPhone 15 Pro. Organizations are invented, and nicknames are never built from person names.
- **Keyword-only arguments**, every one optional, so `rand_name()` on its own works.
- **Seeded or secure.** Every generator and decorator takes a `random`: `Random(42).random` for a fixture that must repeat, `SystemRandom().random` for a token nobody may predict.
- **Pure Python, no dependencies.** It imports nothing outside the standard library and ships a `py.typed` marker, so mypy and Pyright read the annotations.

This is the Python package. The [npm package](https://www.npmjs.com/package/randino) and the [pub.dev package](https://pub.dev/packages/randino) generate from the same datasets under the same rules, and each versions independently.

## Install

```bash
pip install randino
```

Requires **Python 3.10 or newer**. There is nothing else to install.

## Person names

```python
from randino import rand_name

rand_name()
# ['Emma Clover']

rand_name(language="en", count=3)
# ['Christina Mills', 'Jack Reeves', 'Brian Wallace']

rand_name(language="ko", script="roman")
# ['Kim Minjun']

rand_name(language="en", gender="female", include_middle_name=True)
# ['Grace Amelia Bennett']

rand_name(language="ko", output="detail")[0]
# NameDetail(native='여미주', roman='Yeo Miju', language='ko', gender='female')
```

| Argument                    | Type                          | Default    |
| --------------------------- | ----------------------------- | ---------- |
| `language`                  | `NameLanguageOption`          | `"all"`    |
| `gender`                    | `NameGenderOption`            | `"all"`    |
| `count`                     | `int`                         | `1`        |
| `realism`                   | `RandRealism`                 | `"real"`   |
| `min_length` / `max_length` | `int \| None`                 | _language_ |
| `include_surname`           | `bool`                        | `True`     |
| `include_middle_name`       | `bool`                        | `False`    |
| `script`                    | `NameScript`                  | `"native"` |
| `starts_with`               | `str`                         | `""`       |
| `unique`                    | `bool`                        | `False`    |
| `output`                    | `RandOutput`                  | `"value"`  |

`output="detail"` returns a `NameDetail` for each name instead of a string, carrying `native`, `roman`, `language` and `gender`, which makes `script` moot because both forms are already there. The two shapes are `@overload`ed, so a type checker knows which one a call returns.

## Nicknames

```python
from randino import rand_nickname

rand_nickname(language="en", count=3)
# ['FoggyHillside', 'CraneVoyage', 'TinyLeopardCloak']

rand_nickname(language="en", theme="animal", count=2)
# ['FloatingFalcon', 'ChewyOtter']

rand_nickname(language="en", slots="action", count=2)
# ['CountingHarmonics', 'HaulingBurrito']

rand_nickname(language="en", output="detail")[0]
# NicknameDetail(nickname='MistyOwl', words=('Misty', 'Owl'),
#                slots=('adjective', 'noun'), language='en', theme='animal')
```

| Argument                    | Type                              | Default    |
| --------------------------- | --------------------------------- | ---------- |
| `language`                  | `WordLanguageOption`          | `"all"`    |
| `theme`                     | `WordThemeOption`             | `"all"`    |
| `slots`                     | `WordSlotOption`              | `"all"`    |
| `count`                     | `int`                             | `1`        |
| `realism`                   | `RandRealism`                     | `"real"`   |
| `vocabulary`                | `RandVocabulary`                  | `"full"`   |
| `min_length` / `max_length` | `int \| None`                     | _language_ |
| `word_separator`            | `str \| None`                     | _language_ |
| `starts_with`               | `str`                             | `""`       |
| `unique`                    | `bool`                            | `False`    |
| `output`                    | `RandOutput`                      | `"value"`  |

`output="detail"` returns a `NicknameDetail` for each nickname instead of a string, carrying `nickname`, `words`, `slots`, `language` and `theme`.

Themes: `animal`, `object`, `nature`, `plant`, `gem`, `concept`, `myth`, `monster`, `job`, `music`, `genre`, `place`, `building`, `food`, `ingredient`, `vegetable`, `fruit`, `sport`, `vehicle`, `product`, `beauty`, `color`, `finance`, `tech`, `weather`, `space`, `planet`, `time`, `emotion`, `body`, `clothing`, `tool`, `drink`, `toy`, `sound`, `person`, `furniture`.

## Words

The pools the nicknames are built from, on their own. Thirty-seven themes, nine languages, and a function per theme.

```python
from randino import rand_animal, rand_food, rand_word, word_length_range

rand_word(language="en", theme="animal", count=3)
# ['Otter', 'Falcon', 'Lynx']

rand_animal(language="en", count=2)  # ['Turtle', 'Crane']
rand_food(language="en", count=2)  # ['Dumpling', 'Pretzel']

rand_word(language="en", theme="plant", output="detail")
# [WordDetail(word='Cedar', language='en', theme='plant')]

word_length_range("en")  # (3, 11)
```

| Argument                    | Type                             | Default   |
| --------------------------- | -------------------------------- | --------- |
| `language`                  | `WordLanguageOption`             | `"all"`   |
| `theme`                     | `WordThemeOption`                | `"all"`   |
| `count`                     | `int`                            | `1`       |
| `realism`                   | `RandRealism`                    | `"real"`  |
| `vocabulary`                | `RandVocabulary`                 | `"full"`  |
| `min_length` / `max_length` | `int \| None`                    | _pools_   |
| `starts_with`               | `str`                            | `""`      |
| `unique`                    | `bool`                           | `False`   |
| `output`                    | `RandOutput`                     | `"value"` |

One function per theme: `rand_animal`, `rand_object`, `rand_nature`, `rand_plant`, `rand_gem`, `rand_concept`, `rand_myth`, `rand_monster`, `rand_job`, `rand_music`, `rand_genre`, `rand_place`, `rand_building`, `rand_food`, `rand_ingredient`, `rand_vegetable`, `rand_fruit`, `rand_sport`, `rand_vehicle`, `rand_product`, `rand_beauty`, `rand_color`, `rand_finance`, `rand_tech`, `rand_weather`, `rand_space`, `rand_planet`, `rand_time`, `rand_emotion`, `rand_body`, `rand_clothing`, `rand_tool`, `rand_drink`, `rand_toy`, `rand_sound`, `rand_person`, `rand_furniture`. Each is `rand_word` with the theme already chosen.

## Sentences

Whole statements, written the way the language writes them. The nouns are the same pools the words and nicknames come from, and what a sentence adds is the grammar: a verb that states what can do it and what it can be done to, and the shapes each language allows.

```python
from randino import rand_sentence, sentence_length_range

rand_sentence(language="en", count=3)
# ['The brave lion runs quietly.', 'The otter swims in the cove.', 'The sky is blue.']

rand_sentence(language="ko", count=2)
# ['검은 고양이가 숲에서 잠잔다.', '여우가 사과를 먹는다.']

rand_sentence(language="en", shape="simple")  # ['The gondola passes.']
rand_sentence(language="en", include=["brave", "lion"])
# ['The brave lion yawns quietly.']

rand_sentence(language="ko", output="detail")
# [SentenceDetail(sentence='검은 고양이가 숲에서 잠잔다.',
#                 phrases=('검은 고양이', '숲', '잠잔다'),
#                 slots=('subject', 'place', 'verb'), language='ko', theme='animal')]

sentence_length_range("en")  # (11, 117)
```

| Argument                    | Type                                | Default   |
| --------------------------- | ----------------------------------- | --------- |
| `language`                  | `WordLanguageOption`                | `"all"`   |
| `theme`                     | `WordThemeOption`                   | `"all"`   |
| `shape`                     | `SentenceShapeOption`               | `"all"`   |
| `slots`                     | `SentenceSlotOption`                | `"all"`   |
| `include`                   | `str \| Sequence[str]`              | `()`      |
| `type`                      | `SentenceTypeOption \| None`        | _drawn_   |
| `quote`                     | `SentenceQuote \| None`             | `None`    |
| `style`                     | `SentenceStyle \| None`             | _drawn_   |
| `sentences`                 | `int`                               | `1`       |
| `include_name`              | `bool \| None`                      | _drawn_   |
| `count`                     | `int`                               | `1`       |
| `realism`                   | `RandRealism`                       | `"real"`  |
| `vocabulary`                | `RandVocabulary`                    | `"common"`  |
| `min_length` / `max_length` | `int \| None`                       | _language_ |
| `starts_with`               | `str`                               | `""`      |
| `unique`                    | `bool`                              | `False`   |
| `output`                    | `RandOutput`                        | `"value"` |

`slots` names the parts a shape may carry beside its subject: `object`, `place`, `time`, `manner`, `state`, `quantity`, `money`, `date`, `clock`, or `"none"` for a subject and its predicate alone. A language declares its own shapes, so German has no `object` and Russian no `place`, because both would mark those with a case their nouns have to change for. Asking for one falls back to the closest shape the language does have.

`include` puts words you name into every sentence. A word the pools hold goes in the phrase it belongs to, and a word from anywhere else is used as a noun.

`type` is what the sentence does: a statement, a question, an exclamation, a line that trails off, or one somebody says or thinks. `style` is the speech level, which Korean writes four of. `sentences` puts up to ten of them in one string, about one subject. `include_name` puts a generated person's name where a person can stand. Left out, the three of them are drawn per result.

## Locations

Real places, written out from the country down the way the language writes one. Every division is one the country itself publishes, inside the one written beside it, and nothing goes below a Korean 읍·면·동 or a US city, so a result is never somebody's address. Korean and English only: a country is in when its list comes with no conditions a user of this package would inherit.

```python
from randino import rand_city, rand_district, rand_location, rand_region

rand_location(language="ko", count=2)
# ['대한민국 경기도 양평군 단월면', '대한민국 충청북도 청주시 서원구 미평동']
rand_location(language="en", level="city")
# ['Gig Harbor, Washington, United States']

rand_region(language="en", count=3)  # ['Idaho', 'Georgia', 'Vermont']
rand_city(language="ko", count=3)  # ['함안군', '영덕군', '여수시']
rand_district(count=3)  # ['가현동', '겸면', '행주외동']

rand_location(language="ko", include_country=False, short=True)
# ['경기 양평 단월']
rand_region(language="ko", short=True, count=3)  # ['세종', '울산', '충북']

rand_city(language="ko", output="detail")
# [LocationDetail(location='중랑구', language='ko', level='city', country='대한민국',
#                 region='서울특별시', city='중랑구', district=None)]
```

| Argument                    | Type                     | Default      |
| --------------------------- | ------------------------ | ------------ |
| `language`                  | `LocationLanguageOption` | `"all"`      |
| `level`                     | `LocationLevel`          | `"district"` |
| `include_country`           | `bool`                   | `True`       |
| `short`                     | `bool`                   | `False`      |
| `count`                     | `int`                    | `1`          |
| `min_length` / `max_length` | `int \| None`            | `None`       |
| `starts_with`               | `str`                    | `""`         |
| `unique`                    | `bool`                   | `False`      |
| `output`                    | `"value" \| "detail"`    | `"value"`    |

`level` is how far down the location goes: `"country"`, `"region"`, `"city"` or `"district"`. A country without that level stops at the deepest one it has, so an English location ends at its city. `include_country=False` leaves the country out of the string, which is what a fixed `language` usually wants. `short=True` writes each division without its kind, the way it is said: `서울특별시` is `서울`, `충청북도` is `충북` and `관악구` is `관악`, while a name left one character long keeps its kind (`중구`) and English names stay as they are. `rand_region`, `rand_city` and `rand_district` take the same arguments minus `level`, and hand back that one division's name. `rand_country` is the exception: its `language` is any `WordLanguage`, and it names any of the 249 ISO 3166-1 countries and territories in any of the nine languages, with each one's code in a `CountryDetail` when `output="detail"`.

## Ages

Ages for sample people, in whole years. The draw follows a curve shaped like a population rather than an even spread: it peaks from 25 to 35, sits lower for children and falls away past seventy, so a third of the ages are in their twenties and thirties and about 2% are past eighty.

```python
from randino import rand_age

rand_age(count=5)  # [27, 8, 41, 63, 30]
rand_age(min_age=18, max_age=39, count=3)  # [22, 35, 31]
rand_age(group=("teen", "senior"), count=3)  # [15, 71, 66]
rand_age(distribution="uniform", count=3)  # [91, 4, 57]

rand_age(output="detail")  # [AgeDetail(age=16, group='teen')]
```

| Argument              | Type                        | Default        |
| --------------------- | --------------------------- | -------------- |
| `min_age` / `max_age` | `int \| None`               | `0` / `100`    |
| `group`               | `AgeGroupOption`            | `"all"`        |
| `distribution`        | `"population" \| "uniform"` | `"population"` |
| `count`               | `int`                       | `1`            |
| `unique`              | `bool`                      | `False`        |
| `output`              | `"value" \| "detail"`       | `"value"`      |

`group` is `"child"` (0 to 12), `"teen"` (13 to 19), `"adult"` (20 to 64) or `"senior"` (65 and up), or a sequence of them, and narrows the range rather than replacing it. An age has no language, so `rand_age` takes none.

## Genders

Genders for sample people, written the way a form in the language labels them. Male and female split evenly; `include_unknown` adds a gender nobody stated, about one draw in eleven, and `include_nonbinary` a third gender, about one in a hundred. `nonbinary_identity` writes that third gender as the identity a person names themselves by (`Genderqueer`, `Agender`) rather than as the option a form offers.

```python
from randino import rand_gender

rand_gender(language="ko", count=3)  # ['여성', '남성', '여성']
rand_gender(language="en", include_unknown=True, count=3)  # ['Male', 'Unknown', 'Female']
rand_gender(language="de", include_nonbinary=True)  # ['Divers']
rand_gender(language="de", include_nonbinary=True, nonbinary_identity=True)  # ['Genderfluid']

rand_gender(language="ko", output="detail")
# [GenderDetail(gender='여성', code='female', language='ko', identity=None)]
```

| Argument             | Type                  | Default   |
| -------------------- | --------------------- | --------- |
| `language`           | `WordLanguageOption`  | `"all"`   |
| `include_unknown`    | `bool`                | `False`   |
| `include_nonbinary`  | `bool`                | `False`   |
| `nonbinary_identity` | `bool`                | `False`   |
| `count`              | `int`                 | `1`       |
| `unique`             | `bool`                | `False`   |
| `output`             | `"value" \| "detail"` | `"value"` |

The detail's `code` is `"male"`, `"female"`, `"nonbinary"` or `"unknown"` whatever the language, and the first two are the codes `rand_name`'s `gender` takes. Its `identity` names the identity `nonbinary_identity` wrote, the same in every language, and is `None` otherwise.

## Organizations

Companies, schools, government offices, public institutions and associations that do not exist, each written the way its language writes that kind of organization. A company may carry its legal form (`Inc.`, `(주)`, `GmbH`, `ООО`), and the stems are chosen to be nobody's brand.

```python
from randino import rand_organization

rand_organization(language="ko", count=3)  # ['(주)가람에너지', '윤슬교육지원청', '새솔홀딩스']
rand_organization(language="en", industry="logistics")  # ['Greenbriar Logistics Corp.']
rand_organization(language="de", type=("school", "public"), count=2)
# ['Gymnasium Eschenhain', 'Stadtbibliothek Tannenhof']

rand_organization(language="ko", type="company", output="detail")
# [OrganizationDetail(organization='(주)새솔테크', name='새솔테크', legal_form='(주)',
#                     type='company', industry='tech', language='ko')]
```

| Argument                    | Type                             | Default   |
| --------------------------- | -------------------------------- | --------- |
| `language`                  | `WordLanguageOption`             | `"all"`   |
| `type`                      | `OrganizationTypeOption \| None` | `None`    |
| `industry`                  | `OrganizationIndustryOption`     | `"all"`   |
| `include_legal_form`        | `bool \| None`                   | `None`    |
| `count`                     | `int`                            | `1`       |
| `realism`                   | `RandRealism`                    | `"real"`  |
| `min_length` / `max_length` | `int \| None`                    | `None`    |
| `starts_with`               | `str`                            | `""`      |
| `unique`                    | `bool`                           | `False`   |
| `output`                    | `"value" \| "detail"`            | `"value"` |

`type` is `"company"`, `"nonprofit"`, `"school"`, `"government"` or `"public"`, or a sequence of them; left out, companies come up most often. `industry` is one of ten, written into a company's name as a word for its business, and naming one with `type` left out asks for companies. `realism="invented"` builds the stem from the language's own sounds, for a name nobody has.

## Dates

Dates drawn evenly from a range and written out in UTC. The range defaults to the years 1900 to 2099 and the format to ISO 8601. A string bound names a span, so `max_date="2024-12-31"` reaches the last millisecond of that day.

```python
from randino import rand_date

rand_date()  # ['1987-06-21T08:14:51.302Z']
rand_date(min_date="2024-01-01", max_date="2024-12-31", format="YYYY-MM-DD", count=3)
# ['2024-07-09', '2024-02-27', '2024-11-30']
rand_date(format="YYYY년 M월 D일 HH:mm")  # ['2031년 3월 4일 19:40']

rand_date(unit="minute", count=5)  # [37, 4, 52, 19, 0]
rand_date(output="detail")
# [DateDetail(date='1987-06-21T08:14:51.302Z', timestamp=551261691302, year=1987,
#             month=6, day=21, hour=8, minute=14, second=51, millisecond=302)]
```

| Argument                | Type                                | Default                      |
| ----------------------- | ----------------------------------- | ---------------------------- |
| `min_date` / `max_date` | `str \| datetime \| date \| None` | `None` — 1900 / 2099         |
| `format`                | `str`                               | `"YYYY-MM-DDTHH:mm:ss.SSSZ"` |
| `language`              | `WordLanguageOption`                | `"en"`                       |
| `utc_offset`            | `str \| timedelta \| None`          | `None` — UTC                 |
| `unit`                  | `DateUnit \| None`                  | `None`                       |
| `count`                 | `int`                               | `1`                          |
| `unique`                | `bool`                              | `False`                      |
| `output`                | `"value" \| "detail"`               | `"value"`                    |

`format` replaces `YYYY`, `YY`, `MMMM`, `MMM`, `MM`, `M`, `DD`, `D`, `dddd`, `ddd`, `HH`, `H`, `hh`, `h`, `mm`, `m`, `ss`, `s`, `SSS`, `A`, `a`, `Z` and `ZZ`, and writes text inside `[` `]` as it is — the same tokens the npm package takes, rather than `strftime`'s. `MMMM`, `MMM`, `dddd`, `ddd`, `A` and `a` write words, in `language`: English unless another of the nine is named. `utc_offset` writes the dates at a fixed offset, `"+09:00"` or a `timedelta`, and `Z` writes it. `unit` is `"year"`, `"month"`, `"day"`, `"hour"`, `"minute"`, `"second"` or `"millisecond"`, read off a date drawn from the range, and returns an `int` per date.

## Phone numbers

Phone numbers written the way their country writes them, for the United States, Korea, Japan, China, Vietnam, Spain, Italy, Germany and Russia. Each opens on a mobile block or a city's area code that the country really gives out, and the digits after it are random — so a number can by chance belong to somebody. Use them as sample data, and never call or text one. The examples below write the last four digits as `#`; a call returns every digit.

```python
from randino import rand_phone

rand_phone(country="KR", count=2)  # ['010-4821-####', '010-7302-####']
rand_phone(country="US", type="landline")  # ['(212) 846-####']
rand_phone(country="KR", include_country_code=True)  # ['+82 10-4821-####']
rand_phone(country="KR", include_country_code=True, separator="")  # ['+82104821####']

rand_phone(country="JP", output="detail")
# [PhoneDetail(phone='090-3718-####', e164='+81903718####', country='JP',
#              calling_code='81', type='mobile')]
```

| Argument               | Type                  | Default    |
| ---------------------- | --------------------- | ---------- |
| `country`              | `PhoneCountryOption`  | `"all"`    |
| `type`                 | `PhoneTypeOption`     | `"mobile"` |
| `include_country_code` | `bool`                | `False`    |
| `separator`            | `str \| None`         | `None`     |
| `fictional`            | `bool`                | `False`    |
| `count`                | `int`                 | `1`        |
| `unique`               | `bool`                | `False`    |
| `output`               | `"value" \| "detail"` | `"value"`  |

`country` is an ISO 3166-1 alpha-2 code, read regardless of case. `include_country_code` drops the trunk prefix the country dials at home, and `separator` replaces the country's own punctuation: `""` writes the digits alone, which with the country code is E.164. `fictional` keeps to the numbers a country sets aside for films and books, which nobody is given: `555-0100` to `555-0199` in the United States and the drama numbers in Germany; the other seven countries reserve none and return nothing.

## System

Values that describe a sample machine. They are real products rather than invented ones, written by the names they were released under, so none of them takes a `language`.

### Operating systems

Windows, macOS, Ubuntu, Debian and Fedora on the desktop, Android, iOS and iPadOS on mobile, every release from the first to the ones out by October 2026, written the way each release is known.

```python
from randino import rand_os

rand_os(count=3)  # ['Windows 10', 'macOS Sonoma 14', 'Android 9 Pie']
rand_os(platform="mobile")  # ['iOS 17']
rand_os(family=("macos", "ubuntu"))  # ['Ubuntu 22.04 LTS']
rand_os(include_build=True, include_edition=True)  # ['Windows 11 Pro 23H2 (Build 22631)']
rand_os(platform="desktop", max_year=2010)  # ['Mac OS X Snow Leopard 10.6']

rand_os(include_build=True, output="detail")
# [OsDetail(os='Android 14 (API 34)', name='Android', version='14', build='(API 34)',
#           edition=None, family='android', platform='mobile', year=2023)]
```

| Argument          | Type                   | Default   |
| ----------------- | ---------------------- | --------- |
| `platform`        | `SystemPlatformOption` | `"all"`   |
| `family`          | `OsFamilyOption`       | `"all"`   |
| `min_year`        | `int \| None`          | `None`    |
| `max_year`        | `int \| None`          | `None`    |
| `include_version` | `bool`                 | `True`    |
| `include_build`   | `bool`                 | `False`   |
| `include_edition` | `bool`                 | `False`   |
| `count`           | `int`                  | `1`       |
| `unique`          | `bool`                 | `False`   |
| `output`          | `"value" \| "detail"`  | `"value"` |

A draw picks the line first — Windows is about two desktops in three, Android about three phones in five — and a release inside it second. `family` keeps to the lines named (`"windows"`, `"macos"`, `"ubuntu"`, `"fedora"`, `"debian"`, `"android"`, `"ios"`, `"ipados"`), still drawn by how common each is, and a line that does not run on the `platform` asked for returns nothing. `min_year` and `max_year` read the year a release came out, or with `include_build` the year of the build written, and a range nothing came out in returns nothing.

### Devices

Real phones, tablets and laptops, by the names their makers gave them, from the first iPhone to the devices on sale by October 2026. A desktop PC is left out: it is built from parts and has no model name of its own.

```python
from randino import rand_device

rand_device(count=2)  # ['Samsung Galaxy S24 Ultra', 'Apple iPad (10th generation)']
rand_device(type="laptop")  # ['Lenovo ThinkPad X1 Carbon Gen 11']
rand_device(type=("phone", "tablet"), max_year=2012)  # ['Apple iPhone 4']
rand_device(type="phone", include_vendor=False)  # ['Pixel 8 Pro']
rand_device(vendor=("Samsung", "Google"))  # ['Samsung Galaxy Z Fold8']

rand_device(output="detail")
# [DeviceDetail(device='Google Pixel 8', vendor='Google', model='Pixel 8', type='phone', year=2023)]
```

| Argument         | Type                  | Default   |
| ---------------- | --------------------- | --------- |
| `type`           | `DeviceTypeOption`    | `"all"`   |
| `min_year`       | `int \| None`         | `None`    |
| `max_year`       | `int \| None`         | `None`    |
| `vendor`         | `DeviceVendorOption`  | `"all"`   |
| `include_vendor` | `bool`                | `True`    |
| `count`          | `int`                 | `1`       |
| `unique`         | `bool`                | `False`   |
| `output`         | `"value" \| "detail"` | `"value"` |

`type` is `"phone"`, `"tablet"` or `"laptop"`, or a sequence of them, and `vendor` one maker or several, named the way the catalog writes them (`DEVICE_VENDORS`). A model is written the way its maker writes it, generation and all, and one whose name already opens on its maker's (`Xiaomi 14`) is never written with the maker twice.

### Processors

Real processors, by the names their makers gave them: Intel, AMD, Apple and Qualcomm parts for desktops and laptops, and the chips of phones and tablets from Apple, Qualcomm, Samsung, MediaTek, Google and HiSilicon, from the Pentium 4 to the parts on sale by October 2026.

```python
from randino import rand_cpu

rand_cpu(count=2)  # ['Intel Core i7-13700K', 'Apple A17 Pro']
rand_cpu(platform="desktop", max_year=2012)  # ['AMD Phenom II X4 940']
rand_cpu(platform="mobile", include_vendor=False)  # ['Snapdragon 8 Gen 3']
rand_cpu(vendor=("AMD", "Apple"))  # ['AMD Ryzen 5 5600X']

rand_cpu(output="detail")
# [CpuDetail(cpu='Apple M3 Pro', vendor='Apple', model='M3 Pro', platform='desktop', year=2023)]
```

| Argument         | Type                   | Default   |
| ---------------- | ---------------------- | --------- |
| `platform`       | `SystemPlatformOption` | `"all"`   |
| `min_year`       | `int \| None`          | `None`    |
| `max_year`       | `int \| None`          | `None`    |
| `vendor`         | `CpuVendorOption`      | `"all"`   |
| `include_vendor` | `bool`                 | `True`    |
| `count`          | `int`                  | `1`       |
| `unique`         | `bool`                 | `False`   |
| `output`         | `"value" \| "detail"`  | `"value"` |

### Graphics

Real graphics processors, by the names their makers gave them: NVIDIA, AMD and Intel cards, laptop GPUs and integrated graphics for desktops and laptops, and the GPUs of phones and tablets from Qualcomm, Arm and Samsung, from the GeForce 8800 GTX to the parts on sale by October 2026. A Radeon from before the end of 2010 is written as ATI sold it.

```python
from randino import rand_gpu

rand_gpu(count=2)  # ['NVIDIA GeForce RTX 3060', 'Qualcomm Adreno 740']
rand_gpu(platform="desktop", max_year=2010)  # ['ATI Radeon HD 4870']

rand_gpu(output="detail")
# [GpuDetail(gpu='Intel Arc A770', vendor='Intel', model='Arc A770', platform='desktop', year=2022)]
```

It takes `rand_cpu`'s arguments: `platform`, `min_year`, `max_year`, `vendor` and `include_vendor`.

### Architectures

Processor architectures, by the names a download page most often lists them under: `x86_64` and `arm64` nearly every time, the 32-bit `x86` and `armv7` the rest. A desktop is mostly `x86_64` and a phone nearly always `arm64`. `include_rare` adds RISC-V, POWER, IBM Z, MIPS, LoongArch and SPARC, about one desktop draw in twenty together.

```python
from randino import rand_architecture

rand_architecture(count=3)  # ['x86_64', 'arm64', 'x86_64']
rand_architecture(platform="mobile")  # ['arm64']
rand_architecture(naming="debian", count=2)  # ['amd64', 'armhf']
rand_architecture(include_rare=True, count=3)  # ['arm64', 'riscv64', 'x86_64']

rand_architecture(output="detail")
# [ArchitectureDetail(architecture='x86_64', code='x86_64', aliases=('amd64', 'x64'), bits=64,
#                     family='x86', rare=False, platform='desktop')]
```

| Argument       | Type                   | Default    |
| -------------- | ---------------------- | ---------- |
| `platform`     | `SystemPlatformOption` | `"all"`    |
| `include_rare` | `bool`                 | `False`    |
| `naming`       | `ArchitectureNaming`   | `"common"` |
| `count`        | `int`                  | `1`        |
| `unique`       | `bool`                 | `False`    |
| `output`       | `"value" \| "detail"`  | `"value"`  |

`naming` writes Debian's package architectures (`amd64`, `armhf`), Go's `GOARCH` (`amd64`, `386`, `arm`) or the kernel's machine names (`aarch64`, `armv7l`) instead. The detail's `code` is the common name whatever the naming, and its `aliases` every other name, Windows' and Node's `x64` included.

### Memory

Amounts of memory a machine is really sold with, from 512 MB to a terabyte, drawn by how common each one is: 8 and 16 GB are most of a sample, and a phone's stop at 24 GB. A size is only written in a unit it is a whole number of, so nothing carries a decimal point; a gigabyte is 1024 megabytes, the way an operating system counts. Each size comes with the kind of memory it was sold as on its platform and in its years: `DDR` to `DDR5` in a desktop or a laptop, `LPDDR` to `LPDDR5X` in a phone or a tablet.

```python
from randino import rand_ram

rand_ram(count=3)  # ['8 GB', '16 GB', '4 GB']
rand_ram(unit="MB")  # ['8192 MB']
rand_ram(min_size=16, max_size=64)  # ['32 GB']
rand_ram(include_unit=False)  # ['16']
rand_ram(platform="mobile", include_type=True)  # ['12 GB LPDDR5X']
rand_ram(platform="desktop", max_year=2010, include_type=True)  # ['4 GB DDR3']

rand_ram(output="detail")
# [RamDetail(ram='16 GB', value=16, unit='GB', bytes=17179869184, type='DDR5', platform='desktop')]
```

| Argument       | Type                   | Default   |
| -------------- | ---------------------- | --------- |
| `platform`     | `SystemPlatformOption` | `"all"`   |
| `min_year`     | `int \| None`          | `None`    |
| `max_year`     | `int \| None`          | `None`    |
| `unit`         | `RamUnitOption`        | `"auto"`  |
| `include_unit` | `bool`                 | `True`    |
| `min_size`     | `int \| None`          | `None`    |
| `max_size`     | `int \| None`          | `None`    |
| `include_type` | `bool`                 | `False`   |
| `count`        | `int`                  | `1`       |
| `unique`       | `bool`                 | `False`   |
| `output`       | `"value" \| "detail"`  | `"value"` |

`unit="auto"` writes each size in the largest unit it is whole in; `"GB"` or `"MB"` keeps to the sizes whole in that unit. `min_size` and `max_size` are in `unit`, or in gigabytes for `"auto"`, and a range no real size is inside returns nothing. `min_year` and `max_year` keep to the kinds of memory machines were sold with in those years, and the sizes they came in; `include_type` writes the kind after the size, and leaves it out of a bare number with `include_unit=False`.

### Storage

The kind of storage a machine has, by the platform it is drawn for: an SSD or a hard disk on a desktop or a laptop, UFS or eMMC on a phone or a tablet.

```python
from randino import rand_disk_type

rand_disk_type(platform="desktop", count=3)  # ['SSD', 'HDD', 'SSD']
rand_disk_type(platform="mobile")  # ['UFS']

rand_disk_type(output="detail")
# [DiskTypeDetail(disk_type='SSD', code='ssd', name='Solid State Drive', platform='desktop')]
```

| Argument   | Type                   | Default   |
| ---------- | ---------------------- | --------- |
| `platform` | `SystemPlatformOption` | `"all"`   |
| `count`    | `int`                  | `1`       |
| `unique`   | `bool`                 | `False`   |
| `output`   | `"value" \| "detail"`  | `"value"` |

`rand_disk_size` writes how much it holds, the way `rand_ram` writes memory: a size a drive is sold with, drawn by how common it is, in a unit it is whole in. A terabyte is 1000 gigabytes, the way the box counts. Each kind of storage is sold in sizes of its own, so an eMMC chip stops at 128 GB and only a hard disk goes past 8 TB.

```python
from randino import rand_disk_size

rand_disk_size(count=3)  # ['1 TB', '256 GB', '2 TB']
rand_disk_size(unit="GB")  # ['1000 GB']
rand_disk_size(unit="TB", min_size=8)  # ['12 TB']
rand_disk_size(type="emmc")  # ['64 GB']

rand_disk_size(output="detail")
# [DiskSizeDetail(size='1 TB', value=1, unit='TB', bytes=1000000000000, type='ssd', platform='desktop')]
```

It takes `rand_ram`'s `unit`, `include_unit`, `min_size` and `max_size`, with `unit` a `DiskUnitOption`: `"MB"`, `"GB"`, `"TB"` or `"auto"`. `type` keeps to the kinds named, one or a sequence of them, and `platform` to the kinds a desktop or a phone uses, a kind the platform does not use returning nothing.

### Resolutions

Screen resolutions, the way a browser reports them, with the common ones most often: 1920x1080 is about a quarter of the desktops, and a phone is written portrait, its width first. A scaled display is the size the system lays things out at, so a 1920x1080 laptop at 125% is `1536x864`.

```python
from randino import rand_resolution

rand_resolution(platform="desktop", count=3)  # ['1920x1080', '2560x1440', '1366x768']
rand_resolution(platform="mobile")  # ['390x844']
rand_resolution(separator="×")  # ['1920×1080']

rand_resolution(output="detail")
# [ResolutionDetail(resolution='1920x1080', width=1920, height=1080, platform='desktop')]
```

It takes `platform`, `separator`, `count`, `unique`, `random` and `output`.

### Versions

Software version numbers, in one of three formats: `semver` (`2.14.3`), `calver`, counted from a year (`2024.3.1`, `24.04`, `2024.03.15`), or `number`, a version that is one number (`42`). Every part is drawn with the small numbers most often, so `0.x` and `x.y.0` come up the way they do in a registry.

```python
from randino import rand_version

rand_version(count=3)  # ['2.14.3', '0.4.0', '1.0.2']
rand_version(format="calver", min_year=2022)  # ['2024.3.1']
rand_version(format="all", prefix="v", count=3)  # ['v1.4.0', 'v24.04', 'v42']
rand_version(include_prerelease=True, count=2)  # ['3.0.0-rc.1', '0.12.2']
```

It takes `format`, `prefix`, `include_prerelease`, `min_year`, `max_year`, `count`, `unique`, `random` and `output`.

### App stores

Real app stores, by the name each is known by, its company in front where the store is called by one: `Google Play Store`, `Apple App Store`, `Samsung Galaxy Store`, `Steam`. Google Play and Apple's App Store are most of the phones, and the Microsoft Store, Steam and the Mac App Store most of the desktops. Google Play is never a desktop's.

```python
from randino import rand_app_store

rand_app_store(platform="mobile", count=3)
# ['Google Play Store', 'Apple App Store', 'Huawei AppGallery']
rand_app_store(platform="desktop", count=2)  # ['Steam', 'Microsoft Store']
rand_app_store(include_company=False)  # ['Galaxy Store']

rand_app_store(output="detail")
# [AppStoreDetail(store='Apple App Store', name='App Store', company='Apple', platform='mobile')]
```

It takes `platform`, `include_company`, `count`, `unique`, `random` and `output`.

### File extensions

File extensions files are really saved with, from fourteen kinds of file: `.pdf`, `.png`, `.mp4`, `.zip`, `.js`. The extensions nearly everybody meets come up most often, and the ones only a few programs write rarely.

```python
from randino import rand_file_extension

rand_file_extension(count=3)  # ['.pdf', '.png', '.mp4']
rand_file_extension(category="image", count=2)  # ['.jpg', '.webp']
rand_file_extension(category=("code", "data"), include_dot=False)  # ['json']

rand_file_extension(output="detail")
# [FileExtensionDetail(extension='.png', name='png', category='image', mime_type='image/png')]
```

`rand_mime_type` draws the MIME types those extensions are served as, once each, by the part in front of the slash:

```python
from randino import rand_mime_type

rand_mime_type(count=2)  # ['application/pdf', 'image/png']
rand_mime_type(type=("audio", "video"))  # ['video/mp4']

rand_mime_type(output="detail")
# [MimeTypeDetail(mime_type='image/jpeg', type='image', subtype='jpeg', extensions=('jpg', 'jpeg'))]
```

It takes `category`, `include_dot`, `count`, `unique`, `random` and `output`.

## Decorators

`rand_suffix`, `rand_prefix` and `rand_modifier` attach something to a string you already have, rather than generating one. They take anything, not just this library's output, which is why none of them is an argument on a generator. Each of them also works with no value at all, handing back the thing it would have attached.

```python
from randino import rand_nickname, rand_prefix, rand_suffix

rand_suffix("MistyOwl")  # 'MistyOwl_nVtRC'
rand_suffix(rand_nickname(language="en", count=2))
# ['RoundSeason_RVBnC', 'RowdyDusk_dwtu5']

rand_prefix("order-4021", length=4, separator="-")  # 'k3Rm-order-4021'
rand_suffix("MistyOwl", length=8, charset="0123456789")  # 'MistyOwl_40218836'
rand_suffix()  # 'nVtRC' — the token on its own
```

| Argument    | Type  | Default    |
| ----------- | ----- | ---------- |
| `length`    | `int` | `5`        |
| `separator` | `str` | `"_"`      |
| `charset`   | `str` | _built-in_ |

A fresh token per value, never one for the batch. The default charset leaves out `0O1lI`, because these end up in names people read aloud and type back in. `value` is positional and optional, the rest keyword-only, and the overloads carry the shape through: a `str` in gives a `str`, a `list[str]` gives a `list[str]`.

`rand_modifier` attaches a word instead of a token, in front of any string:

```python
from randino import rand_animal, rand_modifier

rand_modifier("Owl")  # 'MistyOwl'
rand_modifier("Owl", separator=" ")  # 'Misty Owl'
rand_modifier("Owl", kind="action")  # 'CountingOwl'
rand_modifier()  # 'Misty'

rand_modifier(rand_animal(language="en", count=2))
# ['TwinklingLynx', 'OnyxCrane']
```

| Argument    | Type                         | Default    |
| ----------- | ---------------------------- | ---------- |
| `value`     | `str \| list[str] \| None`   | `None`     |
| `language`  | `WordLanguageOption \| None` | _script_   |
| `realism`   | `RandRealism`                | `"real"`   |
| `kind`      | `ModifierKind \| "all"`      | `"all"`    |
| `separator` | `str \| None`                | _language_ |

With no `language`, the script of the value picks one, so `"고양이"` is never handed an English modifier.

## Helpers and constants

```python
from randino import name_length_range, name_supports_roman, nickname_length_range

name_length_range("ko")  # (2, 3)
name_length_range("en", include_middle_name=True)  # (11, 32)
name_supports_middle_name("ko")  # False
name_supports_roman("en")  # False
nickname_length_range("ko")  # (1, 13)
sentence_length_range("ko")  # (5, 53)
```

`NAME_LANGUAGES`, `WORD_LANGUAGES`, `WORD_THEMES`, `LOCATION_LANGUAGES`, `LOCATION_LEVELS`, `AGE_GROUPS`, `ORGANIZATION_TYPES`, `ORGANIZATION_INDUSTRIES`, `DATE_UNITS`, `PHONE_COUNTRIES`, `PHONE_TYPES`, `SYSTEM_PLATFORMS`, `OS_FAMILY_CODES`, `DEVICE_TYPES`, `DEVICE_VENDORS`, `RAM_UNITS`, `RAM_TYPES`, `DISK_TYPES`, `DISK_UNITS`, `ARCHITECTURES`, `ARCHITECTURE_NAMINGS`, `VERSION_FORMATS`, `FILE_CATEGORIES`, `MIME_TOP_LEVELS`, `CPU_VENDORS` and `GPU_VENDORS` list what the generators accept; `RAND_COUNT_MAX`, `RAND_LENGTH_MIN` / `MAX`, `RAND_SENTENCE_LENGTH_MAX`, `RAND_SENTENCE_COUNT_MAX`, `RAND_LOCATION_LENGTH_MAX`, `RAND_AGE_MAX`, `RAND_ORGANIZATION_LENGTH_MAX`, `AFFIX_LENGTH_DEFAULT` / `MAX`, `AFFIX_SEPARATOR_DEFAULT` and `AFFIX_CHARSET` are the bounds and defaults every argument is clamped to.

## Differences from the npm package

The two generate the same output from the same data, and only the surface is Python's rather than JavaScript's.

| npm                                        | PyPI                                            |
| ------------------------------------------ | ----------------------------------------------- |
| One options object                         | Keyword-only arguments                          |
| `includeSurname`, `minLength`              | `include_surname`, `min_length`                 |
| `language: 'ko'`, `language: 'all'`        | The same strings, as `Literal` types            |
| `[number, number]`                         | `tuple[int, int]`                               |
| `NameDetail` / `NicknameDetail` interfaces | The same two names, as frozen dataclasses       |
| `detail.words` is an array                 | `detail.words` is a tuple                       |

## Development

```bash
uv venv && uv pip install -e ".[dev]"
pytest
ruff check . && ruff format --check .
mypy
```

## Disclaimer

randino draws its values at random, so some will match real things by chance: a person's name, a company, a phone number in service, a nickname already taken. Any such match is a coincidence, and no value refers to a real person or organization. Product names belong to their owners, and randino is not affiliated with any of them. Use the values as sample data, never to impersonate or deceive anyone or to contact a drawn phone number. The software and its output come without warranty, and nobody involved in randino is liable for any use of them. The [full disclaimer](https://randino.cdget.com/guide/disclaimer) has the details.

## License

MIT © [CDGet](https://cdget.com)
