Metadata-Version: 2.4
Name: mukimov
Version: 0.2.0
Summary: Terminal password masking, colors, Markdown and typed environment settings
Author-email: mukimov <mukimov.shop@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/mukimov/mukimov
Keywords: password,getpass,stars,prompt,cli
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: markdown-it-py>=3.0.0
Requires-Dist: wcwidth>=0.2.5
Requires-Dist: python-dotenv>=1.0.0
Dynamic: license-file

# mukimov

A small cross-platform Python library with masked password input, terminal colors, Markdown and typed environment settings.

[![PyPI version](https://img.shields.io/pypi/v/mukimov.svg)](https://pypi.org/project/mukimov/)
[![Python versions](https://img.shields.io/pypi/pyversions/mukimov.svg)](https://pypi.org/project/mukimov/)
[![License: MIT](https://img.shields.io/pypi/l/mukimov.svg)](https://pypi.org/project/mukimov/)
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-blue.svg)](https://pypi.org/project/mukimov/)

Developed by Mukimov Studio

- ⭐ Hidden password input with `stars()`
- 🎨 12 terminal colors with `mukimov.color`
- 📝 Terminal Markdown rendering with `mukimov.text.markdown`
- ⚙️ Typed environment settings and explicit `.env` loading with `mukimov.env` (since 0.1.8)

## What is mukimov?

`mukimov` lets you read a password or any other secret text in the terminal
while showing a mask character instead of the real keystrokes.

For example, the user really types:

```text
123456
```

but the terminal shows:

```text
Password: ******
```

And the function returns the real text `"123456"`.

Starting with `mukimov 0.1.4`, the library also provides `mukimov.color`
with 12 terminal colors — see the Colors sections below.

Starting with `mukimov 0.1.6`, the library also provides `mukimov.text.markdown`,
a Markdown → ANSI terminal renderer — see the Markdown section below.

Starting with `mukimov 0.1.7`, the package also ships a `mukimov` CLI
with `--version` and a cached self-updater — see the CLI / Updates section below.

> **Important:** this is **NOT hashing** and **NOT encryption**.
> The library only visually hides input from people who may be looking
> at the screen. The real password is still returned to your program
> as a plain string.

## New in 0.2.0 🎉

Typed strings and lists, integer bounds, grouped configuration checks,
installation diagnostics and `python -m mukimov` are available in 0.2.0.

```python
from mukimov import env

env.load(".env")
env.check(["SECRET_KEY", "DB_HOST"])
HOST = env.string("HOST", default="localhost")
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=[])
PORT = env.integer("PORT", default=8000, min=1, max=65535)
SECRET_KEY = env.required("SECRET_KEY")
```

```ini
HOST=localhost
ALLOWED_HOSTS=localhost,127.0.0.1,example.com
PORT=8000
SECRET_KEY=your-secret-key
DB_HOST=localhost
```

- `env.string(name, default=...)` возвращает строку без изменения пробелов.
  Без `default` переменная обязательна. Значение `default` должно быть строкой.
- `env.list(name, default=..., separator=",")` возвращает список строк.
  По умолчанию разделитель — запятая; пробелы вокруг элементов и пустые элементы
  удаляются. `separator=";"` задаёт другой разделитель. `default` должен быть
  списком строк; функция возвращает его копию, чтобы результат можно было менять.
  Значение `a,, b,` превращается в `["a", "b"]`, а `,,` — в `[]`.
- `env.integer(name, default=..., min=None, max=None)` проверяет включительные
  границы, в том числе для значения по умолчанию. `min` и `max` — целые числа,
  `min` не может превышать `max`. Например, порт допускается от 1 до 65535.
- Для строк и списков, как и раньше, `default` применяется только при отсутствии
  переменной. Настройка с пустым или пробельным значением вызывает `EnvError`.
  Значения по умолчанию `""` и `[]` допустимы для отсутствующих настроек.
- `env.check(names)` проверяет все указанные имена сразу. При успехе возвращает
  `None`; иначе вызывает `EnvError`, перечисляя отсутствующие или пустые имена.
  Повторы удаляются; пустая коллекция допустима. После `env.load()` ошибка также
  показывает имена из последнего загруженного `.env`. Значения не показываются.
  Эта функция проверяет наличие; типы проверяются соответствующими getter-функциями.

Пример объединённой ошибки:

```text
Обязательные переменные отсутствуют или пусты:
SECRET_KEY
DB_HOST
Переменные в .env:
DB_PASS
SUPER_KEY
```

### CLI diagnostics and module launch

```bash
mukimov doctor
python -m mukimov --version
python -m mukimov --help
python -m mukimov doctor
python -m mukimov update
```

`doctor` works offline and reports the package version, Python interpreter,
platform, package and Scripts directories, dependency availability, and the
launcher selected by PATH. Exit code `0` means no detected problems; `1` means
missing tools or a launcher from another interpreter. It never prints environment
values or reads `.env`, and it does not install, upgrade or change PATH.

Если `mukimov` не распознаётся в PowerShell, выполните `python -m mukimov doctor`.
При отсутствии команды добавьте папку Scripts нужного Python в PATH или активируйте
виртуальное окружение. Новый терминал подхватит изменения PATH. Запуск через
`python -m mukimov` использует именно выбранный Python и не требует отдельной команды
`mukimov` в PATH; библиотека должна быть установлена в этом Python.

## Installation

```bash
pip install mukimov
```

Upgrade to the latest version:

```bash
pip install --upgrade mukimov
```

Or with the built-in updater (uses the same Python/pip that runs `mukimov`):

```bash
mukimov update
```

Check the installed version:

```bash
mukimov --version
pip show mukimov
```

## Quick Start

The simplest possible example:

```python
from mukimov import stars

password = stars("Password: ")
```

What the user sees:

```text
Password: ******
```

If the user typed `123456`, the variable `password` contains:

```python
"123456"
```

## Custom Mask ✨

The second argument controls which character is shown
for every typed symbol:

```python
stars("Password: ", "*")  # Password: ******
stars("Password: ", "/")  # Password: //////
stars("Password: ", "#")  # Password: ######
stars("Password: ", "•")  # Password: ••••••
```

API:

```python
stars(prompt, mask="*")
```

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `prompt`  | `str` | Text displayed before input. |
| `mask`    | `str` | Character shown instead of each typed symbol. Defaults to `"*"`. |

> **Important:** `mask` must contain exactly **ONE** character.

## Full Example

A realistic login prompt:

```python
from mukimov import stars

username = input("Username: ")
password = stars("Password: ")

print(f"Welcome, {username}!")
```

Example terminal session:

```text
Username: Mukimov
Password: ********
Welcome, Mukimov!
```

Note that the real password is never printed — only the mask is shown.

## Colors 🎨

> Available since `mukimov 0.1.4`.

`mukimov.color` provides 12 terminal colors for your CLI output.

Установка актуальной версии:

```bash
pip install -U mukimov
```

Простой пример:

```python
from mukimov.color import red, green, blue

print(red("Error"))
print(green("Success"))
print(blue("Information"))
```

`red()`, `green()` и остальные функции НЕ вызывают `print()` самостоятельно.
Они возвращают ANSI-цветную строку, которую можно передавать
в `print()`, `input()`, `stars()` и другие функции.

Пример:

```python
message = red("Error")
print(message)
```

## Все 12 цветов

| Function | Color |
| --- | --- |
| `red()` | Red |
| `green()` | Green |
| `blue()` | Blue |
| `yellow()` | Yellow |
| `orange()` | Orange |
| `purple()` | Purple |
| `pink()` | Pink |
| `cyan()` | Cyan |
| `lime()` | Lime |
| `gray()` | Gray |
| `white()` | White |
| `gold()` | Gold |

Полный импорт:

```python
from mukimov.color import (
    red,
    green,
    blue,
    yellow,
    orange,
    purple,
    pink,
    cyan,
    lime,
    gray,
    white,
    gold,
)
```

## Colors + input()

```python
from mukimov.color import red, green

username = input(red("Username: "))
password = input(green("Password: "))
```

Цвет применяется к prompt терминала: текст приглашения отображается
цветным, а введённое пользователем значение остаётся обычным.

## Colors + stars()

Один из главных примеров README:

```python
from mukimov import stars
from mukimov.color import cyan, green, red

print(cyan("=== LOGIN ==="))

username = input(green("Username: "))
password = stars(green("Password: "))

if username == "Mukimov":
    print(green("Login successful!"))
else:
    print(red("Invalid username!"))
```

`stars()` продолжает скрывать ввод пароля (показывает mask-символы
вместо реальных нажатий), а цветовая функция отвечает только
за цвет prompt.

## Validation

Функции цветов принимают только `str`.

Например:

```python
red(123)
```

результат:

```text
TypeError: red() expected str, got int
```

Также запрещены пустые строки и строки только из whitespace:

```python
red("")
red("   ")
red("\t")
red("\n")
```

Ожидается:

```text
ValueError: red() text cannot be empty
```

При этом:

```python
red("Username: ")
```

полностью корректно.

Исходный текст сохраняется без `.strip()` — пробелы внутри
нормального текста разрешены и не изменяются.

## ANSI / RESET

Цвет реализован через ANSI escape sequences: каждая функция оборачивает
текст в код своего цвета и автоматически добавляет RESET (`\x1b[0m`)
в конец строки, поэтому цвет не распространяется на последующий вывод.

Например:

```python
print(red("Error"))
print("Normal text")
```

`Normal text` отображается обычным цветом терминала.

Поддержка:

- Windows Terminal
- PowerShell
- Linux terminals

Для цветов не требуются сторонние зависимости — только чистый Python.

## Markdown 🎨

> Available since `mukimov 0.1.6`.

`mukimov.text.markdown` renders Markdown as beautiful ANSI output
directly in your terminal:

```python
from mukimov.text import markdown

markdown.print("# Hello")
markdown.print("**Bold text**")
markdown.print("*Italic text*")
```

Two functions, two different jobs:

```python
markdown.render(...)  # → returns `str` (prints nothing by itself)
markdown.print(...)   # → prints the rendered result to the terminal
```

Example:

```python
text = markdown.render("**Hello**")
print(text)
```

Supported Markdown: headings `H1`–`H6`, `**bold**`, `__bold__`,
`*italic*`, `_italic_`, `***bold + italic***`, `~~strikethrough~~`,
`` `inline code` ``, `> quotes`, unordered lists (`-`, `*`, `+`),
ordered lists (`1.`), `---` rules, `[links](https://...)`, fenced code
blocks with language, and GitHub-style tables with alignment.

Tables are drawn with box characters and correct column widths
(Unicode-aware, ANSI-aware):

```python
markdown.print("""
| Name | Language |
|------|----------|
| Mukimov | Python |
| Alex | Go |
""")
```

Code blocks keep their content untouched (no Markdown inside is
interpreted) and show the language when it is given:

````python
markdown.print("""
```python
def hello(name):
    if name:
        print(f"Hello {name}")
```
""")
````

Markdown combines with `mukimov.color` — your color stays, Markdown
styling is applied on top:

```python
from mukimov.text import markdown
from mukimov.color import green

markdown.print(green("**Success!**"))
```

`Success!` stays green and becomes bold at the same time.

Notes:

- Markdown parsing is done by the small, stable `markdown-it-py`
  dependency (installed automatically with `mukimov`); the ANSI
  terminal renderer itself is implemented inside `mukimov`.
- Full LaTeX math support is planned for a future release: `$...$`
  syntax currently passes through as plain text and never crashes.
- `markdown.render()` / `markdown.print()` accept only `str`
  (otherwise `TypeError`) and return `""` / print an empty line
  for an empty string.

## Environment settings / .env ⚙️

> Available since `mukimov 0.1.8`.

```python
from mukimov import env

env.load(".env")
DEBUG = env.boolean("DEBUG", default=False)
PORT = env.integer("PORT", default=8000)
SECRET_KEY = env.required("SECRET_KEY")
```

Пример файла `.env` (это настройки, Python-код пишется в `.py`):

```ini
DEBUG=true
PORT=8000
SECRET_KEY=your-secret-key
```

- `env.load(path=".env", override=False)` явно загружает UTF-8 файл относительно
  текущей рабочей директории. Возвращает кортеж имён переменных без значений.
  Системные переменные имеют приоритет; `override=True` разрешает заменить их.
  Кавычки, комментарии, `export` и многострочные значения поддерживаются.
  Подстановка `${NAME}` отключена: значения сохраняются буквально.
  Повторное имя в одном файле получает последнее значение.
- `env.boolean(name, default=...)` принимает `true/false`, `1/0`, `yes/no`,
  `on/off`, без учёта регистра и окружающих пробелов.
- `env.integer(name, default=...)` читает целое десятичное число, включая знак.
- `env.required(name)` возвращает непустую строку без изменения её содержимого.
  Отсутствующее, пустое или состоящее только из пробелов значение вызывает
  `env.EnvError` (подкласс `ValueError`).
- `default` используется только для отсутствующей переменной. Пустое или
  неправильное значение вызывает ошибку даже при наличии `default`.
  Без `default` переменная обязательна. Default должен иметь правильный тип.
- Без `env.load()` функции читают только системное окружение. Импорт библиотеки
  сам по себе не читает `.env`. Отсутствующий файл вызывает `FileNotFoundError`.
- Ошибка синтаксиса содержит номер строки, но не её содержимое; файл с ошибкой
  не загружается частично. Имена должны соответствовать `[A-Za-z_][A-Za-z0-9_]*`.

Если после загрузки `.env` в файле есть только:

```ini
DB_PASS=some-password
SUPER_KEY=some-key
```

и `SECRET_KEY` отсутствует также в системном окружении:

```python
env.required("SECRET_KEY")
```

ошибка содержит **только имена**, без паролей и других значений:

```text
Переменная SECRET_KEY в .env отсутствует!
Переменные в .env:
DB_PASS
SUPER_KEY
```

Если нужная переменная существует в системном окружении, она будет использована,
даже если её нет в файле. Список в ошибке относится только к последнему успешно
загруженному файлу, а не ко всем переменным компьютера. При пустом значении ошибка
явно сообщает об этом. `.env` с секретами не следует добавлять в Git или пакет.

Для Django можно указать путь независимо от текущей директории:

```python
from pathlib import Path
from mukimov import env

BASE_DIR = Path(__file__).resolve().parent.parent
env.load(BASE_DIR / ".env")
DEBUG = env.boolean("DEBUG", default=False)
SECRET_KEY = env.required("SECRET_KEY")
```

## Errors

`mukimov` raises clear, descriptive errors **in Russian**.
Each message tells you which parameter is wrong, what value was passed,
why it is wrong, what was expected, and shows a correct example.

Wrong usage:

```python
stars("Password: ", "//")
```

Error:

```text
ValueError: Параметр mask='//' содержит 2 символа. Допускается только 1 символ.
Пример: stars("Password: ", "*")
```

More examples (real messages from the library):

```python
stars("Password: ", "")
```

```text
ValueError: Параметр mask='' пустой. Укажите ровно 1 символ для маскировки.
Пример: stars("Password: ", "*")
```

```python
stars("Password: ", 123)
```

```text
TypeError: Параметр mask должен быть строкой (str), но получен int: 123.
Пример: stars("Password: ", "*")
```

```python
stars(123)
```

```text
TypeError: Параметр prompt должен быть строкой (str), но получен int: 123.
Пример: stars("Password: ")
```

## Platform Support

Only systems actually supported by the current code are listed:

| OS | Backend |
| -- | ------- |
| Windows | `msvcrt` |
| Linux | `termios` / `tty` |
| macOS | `termios` / `tty` |

Password input and colors use only the Python standard library.
Markdown uses `markdown-it-py` and `wcwidth`; `.env` loading uses `python-dotenv`. When standard input is not a TTY (pipes, IDE consoles, CI),
it safely falls back to `getpass` with no echo.

Extra behavior worth knowing:

- Backspace deletes the last mask character and the last symbol.
- Enter finishes input.
- `Ctrl+C` raises `KeyboardInterrupt`, `Ctrl+D` on empty input raises `EOFError`.

## Why mukimov?

The goal of `mukimov` is a simple, short API for masked terminal input.

mukimov:

```python
from mukimov import stars

password = stars("Password: ", "•")
```

And the standard-library alternative, where your Python version
supports `echo_char`:

```python
from getpass import getpass

password = getpass("Password: ", echo_char="•")
```

If you like the classic `getpass`, keep using it — `mukimov` is just
a compact option when you want per-character masking with a tiny API.

## Security 🔒

`mukimov` hides the password **only visually, while it is being typed**.

It does NOT:

- hash the password
- encrypt the password
- store the password securely by itself
- protect the process from malware
- protect the contents of the process memory

After input, the function returns the real password as a regular
Python string. To store passwords, developers must separately use
a reliable password-hashing solution.

Also, error messages and logs of this library never include
the typed password — only configuration values (`prompt`, `mask`)
may appear in validation errors.

## API

```python
stars(prompt="Password: ", mask="*")
```

Parameters:

- `prompt: str` — text displayed before input.
- `mask: str` — exactly one character shown instead of each typed symbol.

Returns:

- `str` — the real text the user typed.

Exceptions:

- `TypeError` — `prompt` or `mask` is not a string.
- `ValueError` — `mask` is empty or longer than one character.

### Color API

```python
red(text: str) -> str
green(text: str) -> str
blue(text: str) -> str
yellow(text: str) -> str
orange(text: str) -> str
purple(text: str) -> str
pink(text: str) -> str
cyan(text: str) -> str
lime(text: str) -> str
gray(text: str) -> str
white(text: str) -> str
gold(text: str) -> str
```

Each color function takes a string and returns it wrapped in its ANSI
color code with RESET applied at the end. Raises `TypeError` for
non-string input and `ValueError` for empty/whitespace-only strings.

### Markdown API

```python
from mukimov.text import markdown

markdown.render(text: str) -> str   # render Markdown to an ANSI string
markdown.print(text: str) -> None   # render and print to the terminal
```

`render()` returns the styled string without printing anything;
`print()` writes the rendered result to the terminal.
Both raise `TypeError` for non-string input
(e.g. `markdown.render() expected str, got int`).
LaTeX math rendering is planned for a future release.

## CLI / Updates 🔄

> Available since `mukimov 0.1.7`.

Show the installed version:

```bash
mukimov --version
```

```text
mukimov 0.2.0
```

Check PyPI and upgrade to the latest release (no confirmation asked):

```bash
mukimov update
```

If a newer version exists, `mukimov` upgrades the current installation with
the same interpreter that runs it (`python -m pip install --upgrade mukimov`)
and prints the old and new versions:

```text
Updated mukimov: 0.1.8 → 0.2.0
```

If the latest version is already installed:

```text
Already up to date (обновление не требуется): mukimov 0.2.0
```

When a new release is known, other CLI invocations print a reminder
to stderr (stdout stays script-friendly):

```text
⚠ New mukimov version available: 0.2.0
Run: mukimov update
```

Notes:

- The PyPI check is cached on disk for 24 hours (`%LOCALAPPDATA%\mukimov`
  on Windows, `~/.cache/mukimov` elsewhere), so repeated CLI calls
  do not hammer PyPI.
- Plain `import mukimov` never touches the network.
- The library is never upgraded automatically — only `mukimov update` does.
- Network/PyPI/pip problems print a clear error and exit with code `1`
  without tracebacks.

## Project

- PyPI: https://pypi.org/project/mukimov/
- GitHub: https://github.com/mukimov/mukimov
- Author: mukimov
- Organization: Mukimov Studio
- Email: mukimov.shop@gmail.com
- License: MIT
