pybasedb-json 0.4.0__tar.gz → 0.5.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 (40) hide show
  1. pybasedb_json-0.5.0/PKG-INFO +173 -0
  2. pybasedb_json-0.5.0/README.md +147 -0
  3. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/__init__.py +4 -0
  4. pybasedb_json-0.5.0/pybase/__main__.py +5 -0
  5. pybasedb_json-0.5.0/pybase/async_.py +66 -0
  6. pybasedb_json-0.5.0/pybase/cli/__init__.py +3 -0
  7. pybasedb_json-0.5.0/pybase/cli/colors.py +122 -0
  8. pybasedb_json-0.5.0/pybase/cli/commands.py +256 -0
  9. pybasedb_json-0.5.0/pybase/cli/format.py +92 -0
  10. pybasedb_json-0.5.0/pybase/cli/main.py +151 -0
  11. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/collections.py +200 -36
  12. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/database.py +29 -2
  13. pybasedb_json-0.5.0/pybase/filters.py +66 -0
  14. pybasedb_json-0.5.0/pybase/index.py +102 -0
  15. pybasedb_json-0.5.0/pybase/lock.py +48 -0
  16. pybasedb_json-0.5.0/pybase/migration.py +47 -0
  17. pybasedb_json-0.5.0/pybase/plugin.py +48 -0
  18. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/query.py +5 -0
  19. pybasedb_json-0.5.0/pybase/ref.py +31 -0
  20. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/server.py +140 -7
  21. pybasedb_json-0.5.0/pybase/transaction.py +51 -0
  22. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/validation.py +284 -11
  23. pybasedb_json-0.5.0/pybasedb_json.egg-info/PKG-INFO +173 -0
  24. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybasedb_json.egg-info/SOURCES.txt +14 -0
  25. pybasedb_json-0.5.0/pybasedb_json.egg-info/entry_points.txt +2 -0
  26. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pyproject.toml +4 -1
  27. pybasedb_json-0.4.0/PKG-INFO +0 -146
  28. pybasedb_json-0.4.0/README.md +0 -120
  29. pybasedb_json-0.4.0/pybase/index.py +0 -54
  30. pybasedb_json-0.4.0/pybasedb_json.egg-info/PKG-INFO +0 -146
  31. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/backup.py +0 -0
  32. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/crypto.py +0 -0
  33. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/exceptions.py +0 -0
  34. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/hooks.py +0 -0
  35. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/media.py +0 -0
  36. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/pybase.py +0 -0
  37. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybase/utils.py +0 -0
  38. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybasedb_json.egg-info/dependency_links.txt +0 -0
  39. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/pybasedb_json.egg-info/top_level.txt +0 -0
  40. {pybasedb_json-0.4.0 → pybasedb_json-0.5.0}/setup.cfg +0 -0
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.4
2
+ Name: pybasedb-json
3
+ Version: 0.5.0
4
+ Summary: Banco de dados local com JSON — schemas, decorator API, server REST, criptografia, zero dependencias
5
+ Author-email: Marcos Gomes <marcosgabrielgomes110@gmail.com>
6
+ Maintainer-email: Marcos Gomes <marcosgabrielgomes110@gmail.com>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/marcosgabrielgomes110-collab/Pybase
9
+ Project-URL: Documentation, https://github.com/marcosgabrielgomes110-collab/Pybase/tree/main/docs
10
+ Project-URL: Repository, https://github.com/marcosgabrielgomes110-collab/Pybase
11
+ Keywords: database,json,local,embedded,nosql,thread-safe,rest-api,schema,media
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Database
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+
27
+ <p align="center">
28
+ <img src="assets/logo/logo.png" width="120" alt="Pybase logo">
29
+ </p>
30
+
31
+ # Pybase v0.5
32
+
33
+ Base para criacao de bancos de dados locais com JSON. Zero dependencias, 100% stdlib.
34
+ Operacoes de escrita atomicas, thread-safe.
35
+
36
+ Filosofia **Lego**: cada modulo e independente — voce monta o que precisa.
37
+
38
+ ## Instalacao
39
+
40
+ ```bash
41
+ pip install pybasedb-json
42
+ ```
43
+
44
+ Para o servidor REST (opcional):
45
+ ```bash
46
+ pip install Flask
47
+ ```
48
+
49
+ ## Uso rapido
50
+
51
+ ```python
52
+ from pybase import pybase as pb
53
+
54
+ # Banco de dados
55
+ db = pb.Database("./data/mydb")
56
+
57
+ # Schema com decorator
58
+ @db.schema.users
59
+ class User:
60
+ name: str
61
+ email: str
62
+ age: int = 0
63
+ active: bool = True
64
+ tags: list[str] = []
65
+
66
+ # Colecao com validacao automatica
67
+ users = db["users"]
68
+ users.add({"name": "marcos", "email": "m@m.com"})
69
+
70
+ # Operadores de query (v0.5+)
71
+ users.find(age__gt=18, name__contains="mar")
72
+
73
+ # Paginacao (v0.5+)
74
+ users.paginate(page=1, per_page=20)
75
+
76
+ # Transacao (v0.5+)
77
+ with db.transaction() as txn:
78
+ txn["users"].add({"name": "ana"})
79
+
80
+ # Server com auth + CORS (v0.5+)
81
+ pb.Server(db, auth="token123", cors="*").run()
82
+
83
+ # CLI (v0.5+)
84
+ # $ pybase serve mydb --port=4560
85
+ ```
86
+
87
+ ## Modulos
88
+
89
+ | Modulo | Arquivo | Descricao | Deps |
90
+ |--------|---------|-----------|------|
91
+ | Database | `pybase/database.py` | Gerencia diretorio de tabelas JSON | 0 |
92
+ | Collection | `pybase/collections.py` | CRUD atomico + query com operadores + JSONL | 0 |
93
+ | Filter | `pybase/filters.py` | Motor de filtros com 13 operadores | 0 |
94
+ | Query | `pybase/query.py` | Thin wrapper sobre Collection | 0 |
95
+ | Schema | `pybase/validation.py` | Validacao com choices, min/max, email, datetime, UUID, ref, JSON Schema | 0 |
96
+ | Ref | `pybase/ref.py` | Referencias entre colecoes | 0 |
97
+ | Server | `pybase/server.py` | REST API com auth, cors, patch, batch + Swagger UI | Flask |
98
+ | CLI | `pybase/cli/` | Linha de comando: init, inspect, serve, backup (ASCII + ANSI) | 0 |
99
+ | Media | `pybase/media.py` | Upload/download de imagens | 0 |
100
+ | Backup | `pybase/backup.py` | Snapshot e restore (seletivo) | 0 |
101
+ | Crypto | `pybase/crypto.py` | Criptografia stdlib (scrypt + XOR) | 0 |
102
+ | Hooks | `pybase/hooks.py` | Lifecycle hooks para Collection | 0 |
103
+ | Plugin | `pybase/plugin.py` | Sistema de plugins globais | 0 |
104
+ | Transaction | `pybase/transaction.py` | Multi-colecao commit/rollback | 0 |
105
+ | Migration | `pybase/migration.py` | Migracoes versionadas | 0 |
106
+ | Async | `pybase/async_.py` | Wrappers assincronos | 0 |
107
+ | Index | `pybase/index.py` | Indices hash O(1) (memoria + persistente) | 0 |
108
+
109
+ ## Novidades da v0.5
110
+
111
+ - **Operadores de query** — `__gt`, `__contains`, `__in`, `__has`, `__regex` e mais 13
112
+ - **Paginacao** — `.paginate(page, per_page)` → `{items, total, pages}`
113
+ - **Projecao** — `.select("name", "age")` para campos especificos
114
+ - **Agregacao** — `.aggregate([$match, $group, $sort, ...])`
115
+ - **Schema avancado** — `choices`, `min`/`max`, `email`, `url`, `datetime`, `UUID`, `ref`
116
+ - **Ref** — `Ref("users", "abc").resolve(db)` para referencias entre colecoes
117
+ - **JSON Schema** — `.to_json_schema()` export draft-07
118
+ - **CLI** — `pybase init`, `inspect`, `serve`, `backup`, `query`, `schema`, ...
119
+ - **Server auth** — `Server(db, auth="token")`, Basic Auth, callback
120
+ - **Server CORS** — `Server(db, cors="*")` ou lista de origens
121
+ - **PATCH** — atualizacao parcial via REST
122
+ - **Batch** — operacoes em lote via REST
123
+ - **Transacoes** — `with db.transaction() as txn:` commit/rollback
124
+ - **Plugins** — `Plugin` class com `on_add`, `on_update`, etc
125
+ - **Migracoes** — `Migration(db).add(ver, fn).run()`
126
+ - **Async** — `AsyncCollection`, `AsyncDatabase` (via `asyncio.to_thread`)
127
+ - **Eventos** — `col.on("add", cb)` observer pattern
128
+ - **JSONL** — formato linha-por-linha para append O(1)
129
+ - **Lock multi-processo** — `Collection(lock_type="process")`
130
+ - **PersistentIndex** — indice salvo em disco
131
+ - **Clone** — `db.clone(dest)` copia banco
132
+ - **Context manager** — `with Database(...) as db:`
133
+
134
+ ## Estrutura
135
+
136
+ ```
137
+ Pybase/
138
+ pybase/
139
+ __init__.py # Database, Collection, Query, Server, Schema, T, Filter, Ref
140
+ __main__.py # python -m pybase
141
+ pybase.py # from pybase import pybase
142
+ database.py # Database, SchemaRegistrar
143
+ collections.py # Collection, DocRef, QueryBuilder, SchemaCollection
144
+ filters.py # Filter: operadores de query
145
+ query.py # Query wrapper
146
+ exceptions.py # PybaseError > DatabaseError, CollectionError, ...
147
+ validation.py # Schema, T, ValidationError
148
+ ref.py # Ref: referencias entre colecoes
149
+ server.py # Server REST (Flask) com auth/cors/patch/batch
150
+ cli/ # CLI: init, inspect, serve, backup (ASCII + ANSI)
151
+ media.py # MediaManager
152
+ index.py # Index + PersistentIndex
153
+ hooks.py # HooksCollection
154
+ backup.py # take, restore
155
+ crypto.py # encrypt, decrypt (stdlib)
156
+ plugin.py # Plugin system
157
+ transaction.py # Transaction commit/rollback
158
+ migration.py # Migration versionada
159
+ async_.py # AsyncCollection, AsyncDatabase
160
+ lock.py # FileLock multi-processo
161
+ utils.py # PBKDF2 encode/verify
162
+ docs/ # Documentacao completa (23 paginas)
163
+ index.html # Documentacao web
164
+ pyproject.toml
165
+ ```
166
+
167
+ ## Documentacao
168
+
169
+ Veja [docs/](docs/index.md) ou [index.html](index.html) com referencia completa de cada classe e metodo.
170
+
171
+ ## Licenca
172
+
173
+ MIT
@@ -0,0 +1,147 @@
1
+ <p align="center">
2
+ <img src="assets/logo/logo.png" width="120" alt="Pybase logo">
3
+ </p>
4
+
5
+ # Pybase v0.5
6
+
7
+ Base para criacao de bancos de dados locais com JSON. Zero dependencias, 100% stdlib.
8
+ Operacoes de escrita atomicas, thread-safe.
9
+
10
+ Filosofia **Lego**: cada modulo e independente — voce monta o que precisa.
11
+
12
+ ## Instalacao
13
+
14
+ ```bash
15
+ pip install pybasedb-json
16
+ ```
17
+
18
+ Para o servidor REST (opcional):
19
+ ```bash
20
+ pip install Flask
21
+ ```
22
+
23
+ ## Uso rapido
24
+
25
+ ```python
26
+ from pybase import pybase as pb
27
+
28
+ # Banco de dados
29
+ db = pb.Database("./data/mydb")
30
+
31
+ # Schema com decorator
32
+ @db.schema.users
33
+ class User:
34
+ name: str
35
+ email: str
36
+ age: int = 0
37
+ active: bool = True
38
+ tags: list[str] = []
39
+
40
+ # Colecao com validacao automatica
41
+ users = db["users"]
42
+ users.add({"name": "marcos", "email": "m@m.com"})
43
+
44
+ # Operadores de query (v0.5+)
45
+ users.find(age__gt=18, name__contains="mar")
46
+
47
+ # Paginacao (v0.5+)
48
+ users.paginate(page=1, per_page=20)
49
+
50
+ # Transacao (v0.5+)
51
+ with db.transaction() as txn:
52
+ txn["users"].add({"name": "ana"})
53
+
54
+ # Server com auth + CORS (v0.5+)
55
+ pb.Server(db, auth="token123", cors="*").run()
56
+
57
+ # CLI (v0.5+)
58
+ # $ pybase serve mydb --port=4560
59
+ ```
60
+
61
+ ## Modulos
62
+
63
+ | Modulo | Arquivo | Descricao | Deps |
64
+ |--------|---------|-----------|------|
65
+ | Database | `pybase/database.py` | Gerencia diretorio de tabelas JSON | 0 |
66
+ | Collection | `pybase/collections.py` | CRUD atomico + query com operadores + JSONL | 0 |
67
+ | Filter | `pybase/filters.py` | Motor de filtros com 13 operadores | 0 |
68
+ | Query | `pybase/query.py` | Thin wrapper sobre Collection | 0 |
69
+ | Schema | `pybase/validation.py` | Validacao com choices, min/max, email, datetime, UUID, ref, JSON Schema | 0 |
70
+ | Ref | `pybase/ref.py` | Referencias entre colecoes | 0 |
71
+ | Server | `pybase/server.py` | REST API com auth, cors, patch, batch + Swagger UI | Flask |
72
+ | CLI | `pybase/cli/` | Linha de comando: init, inspect, serve, backup (ASCII + ANSI) | 0 |
73
+ | Media | `pybase/media.py` | Upload/download de imagens | 0 |
74
+ | Backup | `pybase/backup.py` | Snapshot e restore (seletivo) | 0 |
75
+ | Crypto | `pybase/crypto.py` | Criptografia stdlib (scrypt + XOR) | 0 |
76
+ | Hooks | `pybase/hooks.py` | Lifecycle hooks para Collection | 0 |
77
+ | Plugin | `pybase/plugin.py` | Sistema de plugins globais | 0 |
78
+ | Transaction | `pybase/transaction.py` | Multi-colecao commit/rollback | 0 |
79
+ | Migration | `pybase/migration.py` | Migracoes versionadas | 0 |
80
+ | Async | `pybase/async_.py` | Wrappers assincronos | 0 |
81
+ | Index | `pybase/index.py` | Indices hash O(1) (memoria + persistente) | 0 |
82
+
83
+ ## Novidades da v0.5
84
+
85
+ - **Operadores de query** — `__gt`, `__contains`, `__in`, `__has`, `__regex` e mais 13
86
+ - **Paginacao** — `.paginate(page, per_page)` → `{items, total, pages}`
87
+ - **Projecao** — `.select("name", "age")` para campos especificos
88
+ - **Agregacao** — `.aggregate([$match, $group, $sort, ...])`
89
+ - **Schema avancado** — `choices`, `min`/`max`, `email`, `url`, `datetime`, `UUID`, `ref`
90
+ - **Ref** — `Ref("users", "abc").resolve(db)` para referencias entre colecoes
91
+ - **JSON Schema** — `.to_json_schema()` export draft-07
92
+ - **CLI** — `pybase init`, `inspect`, `serve`, `backup`, `query`, `schema`, ...
93
+ - **Server auth** — `Server(db, auth="token")`, Basic Auth, callback
94
+ - **Server CORS** — `Server(db, cors="*")` ou lista de origens
95
+ - **PATCH** — atualizacao parcial via REST
96
+ - **Batch** — operacoes em lote via REST
97
+ - **Transacoes** — `with db.transaction() as txn:` commit/rollback
98
+ - **Plugins** — `Plugin` class com `on_add`, `on_update`, etc
99
+ - **Migracoes** — `Migration(db).add(ver, fn).run()`
100
+ - **Async** — `AsyncCollection`, `AsyncDatabase` (via `asyncio.to_thread`)
101
+ - **Eventos** — `col.on("add", cb)` observer pattern
102
+ - **JSONL** — formato linha-por-linha para append O(1)
103
+ - **Lock multi-processo** — `Collection(lock_type="process")`
104
+ - **PersistentIndex** — indice salvo em disco
105
+ - **Clone** — `db.clone(dest)` copia banco
106
+ - **Context manager** — `with Database(...) as db:`
107
+
108
+ ## Estrutura
109
+
110
+ ```
111
+ Pybase/
112
+ pybase/
113
+ __init__.py # Database, Collection, Query, Server, Schema, T, Filter, Ref
114
+ __main__.py # python -m pybase
115
+ pybase.py # from pybase import pybase
116
+ database.py # Database, SchemaRegistrar
117
+ collections.py # Collection, DocRef, QueryBuilder, SchemaCollection
118
+ filters.py # Filter: operadores de query
119
+ query.py # Query wrapper
120
+ exceptions.py # PybaseError > DatabaseError, CollectionError, ...
121
+ validation.py # Schema, T, ValidationError
122
+ ref.py # Ref: referencias entre colecoes
123
+ server.py # Server REST (Flask) com auth/cors/patch/batch
124
+ cli/ # CLI: init, inspect, serve, backup (ASCII + ANSI)
125
+ media.py # MediaManager
126
+ index.py # Index + PersistentIndex
127
+ hooks.py # HooksCollection
128
+ backup.py # take, restore
129
+ crypto.py # encrypt, decrypt (stdlib)
130
+ plugin.py # Plugin system
131
+ transaction.py # Transaction commit/rollback
132
+ migration.py # Migration versionada
133
+ async_.py # AsyncCollection, AsyncDatabase
134
+ lock.py # FileLock multi-processo
135
+ utils.py # PBKDF2 encode/verify
136
+ docs/ # Documentacao completa (23 paginas)
137
+ index.html # Documentacao web
138
+ pyproject.toml
139
+ ```
140
+
141
+ ## Documentacao
142
+
143
+ Veja [docs/](docs/index.md) ou [index.html](index.html) com referencia completa de cada classe e metodo.
144
+
145
+ ## Licenca
146
+
147
+ MIT
@@ -5,6 +5,8 @@ except ImportError:
5
5
 
6
6
  from .collections import Collection, DocRef, QueryBuilder, SchemaCollection
7
7
  from .database import Database
8
+ from .filters import Filter
9
+ from .ref import Ref
8
10
  from .exceptions import (
9
11
  CollectionError,
10
12
  DatabaseError,
@@ -33,9 +35,11 @@ __all__ = [
33
35
  "PybaseError",
34
36
  "DatabaseError",
35
37
  "CollectionError",
38
+ "Filter",
36
39
  "Query",
37
40
  "QueryBuilder",
38
41
  "QueryError",
42
+ "Ref",
39
43
  "Schema",
40
44
  "SchemaCollection",
41
45
  "Server",
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ from .cli import main
4
+
5
+ sys.exit(main())
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from typing import Any
5
+
6
+ from .collections import Collection as SyncCollection
7
+
8
+
9
+ class AsyncCollection:
10
+ def __init__(self, collection: SyncCollection) -> None:
11
+ self._col = collection
12
+
13
+ async def all(self) -> list[dict]:
14
+ return await asyncio.to_thread(self._col.all)
15
+
16
+ async def get(self, doc_id: str) -> dict | None:
17
+ return await asyncio.to_thread(self._col.get, doc_id)
18
+
19
+ async def add(self, data: dict) -> dict:
20
+ return await asyncio.to_thread(self._col.add, data)
21
+
22
+ async def add_many(self, items: list[dict]) -> list[dict]:
23
+ return await asyncio.to_thread(self._col.add_many, items)
24
+
25
+ async def update(self, data: dict, **match) -> int:
26
+ return await asyncio.to_thread(self._col.update, data, **match)
27
+
28
+ async def delete(self, **match) -> int:
29
+ return await asyncio.to_thread(self._col.delete, **match)
30
+
31
+ async def find(self, **match) -> list[dict]:
32
+ return await asyncio.to_thread(self._col.find, **match)
33
+
34
+ async def find_one(self, **match) -> dict | None:
35
+ return await asyncio.to_thread(self._col.find_one, **match)
36
+
37
+ async def exists(self, **match) -> bool:
38
+ return await asyncio.to_thread(self._col.exists, **match)
39
+
40
+ async def count(self, **match) -> int:
41
+ return await asyncio.to_thread(self._col.count, **match)
42
+
43
+ async def paginate(self, page: int = 1, per_page: int = 20) -> dict:
44
+ return await asyncio.to_thread(self._col.paginate, page, per_page)
45
+
46
+ def __getattr__(self, name: str):
47
+ return getattr(self._col, name)
48
+
49
+
50
+ class AsyncDatabase:
51
+ def __init__(self, db) -> None:
52
+ self._db = db
53
+
54
+ @property
55
+ def name(self) -> str:
56
+ return self._db.name
57
+
58
+ @property
59
+ def collections(self) -> list[str]:
60
+ return self._db.collections
61
+
62
+ def collection(self, name: str) -> AsyncCollection:
63
+ return AsyncCollection(self._db.collection(name))
64
+
65
+ async def info(self) -> dict:
66
+ return await asyncio.to_thread(self._db.info)
@@ -0,0 +1,3 @@
1
+ from .main import main
2
+
3
+ __all__ = ["main"]
@@ -0,0 +1,122 @@
1
+ import os
2
+
3
+ _RESET = "\033[0m"
4
+ _BOLD = "\033[1m"
5
+ _DIM = "\033[2m"
6
+ _ITALIC = "\033[3m"
7
+ _UNDERLINE = "\033[4m"
8
+ _REVERSE = "\033[7m"
9
+
10
+ _BLACK = "\033[30m"
11
+ _RED = "\033[31m"
12
+ _GREEN = "\033[32m"
13
+ _YELLOW = "\033[33m"
14
+ _BLUE = "\033[34m"
15
+ _MAGENTA = "\033[35m"
16
+ _CYAN = "\033[36m"
17
+ _WHITE = "\033[37m"
18
+
19
+ _BG_BLACK = "\033[40m"
20
+ _BG_RED = "\033[41m"
21
+ _BG_GREEN = "\033[42m"
22
+ _BG_YELLOW = "\033[43m"
23
+ _BG_BLUE = "\033[44m"
24
+ _BG_MAGENTA = "\033[45m"
25
+ _BG_CYAN = "\033[46m"
26
+ _BG_WHITE = "\033[47m"
27
+
28
+
29
+ def _supports_color() -> bool:
30
+ return os.environ.get("NO_COLOR") is None and (
31
+ os.environ.get("TERM") not in ("dumb", "") or os.isatty(1)
32
+ )
33
+
34
+
35
+ def s(text: str, *codes: str) -> str:
36
+ if not codes or not _supports_color():
37
+ return text
38
+ return "".join(codes) + text + _RESET
39
+
40
+
41
+ def bold(text: str) -> str:
42
+ return s(text, _BOLD)
43
+
44
+
45
+ def dim(text: str) -> str:
46
+ return s(text, _DIM)
47
+
48
+
49
+ def red(text: str) -> str:
50
+ return s(text, _RED)
51
+
52
+
53
+ def green(text: str) -> str:
54
+ return s(text, _GREEN)
55
+
56
+
57
+ def yellow(text: str) -> str:
58
+ return s(text, _YELLOW)
59
+
60
+
61
+ def blue(text: str) -> str:
62
+ return s(text, _BLUE)
63
+
64
+
65
+ def magenta(text: str) -> str:
66
+ return s(text, _MAGENTA)
67
+
68
+
69
+ def cyan(text: str) -> str:
70
+ return s(text, _CYAN)
71
+
72
+
73
+ def white(text: str) -> str:
74
+ return s(text, _WHITE)
75
+
76
+
77
+ def bold_red(text: str) -> str:
78
+ return s(text, _BOLD, _RED)
79
+
80
+
81
+ def bold_green(text: str) -> str:
82
+ return s(text, _BOLD, _GREEN)
83
+
84
+
85
+ def bold_yellow(text: str) -> str:
86
+ return s(text, _BOLD, _YELLOW)
87
+
88
+
89
+ def bold_blue(text: str) -> str:
90
+ return s(text, _BOLD, _BLUE)
91
+
92
+
93
+ def bold_cyan(text: str) -> str:
94
+ return s(text, _BOLD, _CYAN)
95
+
96
+
97
+ def bold_magenta(text: str) -> str:
98
+ return s(text, _BOLD, _MAGENTA)
99
+
100
+
101
+ def header(text: str) -> str:
102
+ return s(text, _BOLD, _WHITE, _BG_BLUE)
103
+
104
+
105
+ def ok(text: str) -> str:
106
+ return s(text, _BOLD, _GREEN)
107
+
108
+
109
+ def warn(text: str) -> str:
110
+ return s(text, _BOLD, _YELLOW)
111
+
112
+
113
+ def fail(text: str) -> str:
114
+ return s(text, _BOLD, _RED)
115
+
116
+
117
+ def info(text: str) -> str:
118
+ return s(text, _CYAN)
119
+
120
+
121
+ def muted(text: str) -> str:
122
+ return s(text, _DIM)