codeinsult 0.1.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.
- codeinsult-0.1.0/LICENSE +21 -0
- codeinsult-0.1.0/PKG-INFO +113 -0
- codeinsult-0.1.0/codeinsult/CodeInsult.py +107 -0
- codeinsult-0.1.0/codeinsult/__init__.py +13 -0
- codeinsult-0.1.0/codeinsult/__version__.py +4 -0
- codeinsult-0.1.0/codeinsult/config.py +33 -0
- codeinsult-0.1.0/codeinsult/messages/__init__.py +4 -0
- codeinsult-0.1.0/codeinsult/messages/base.py +78 -0
- codeinsult-0.1.0/codeinsult/messages/pt_br.py +457 -0
- codeinsult-0.1.0/codeinsult/messages/registry.py +34 -0
- codeinsult-0.1.0/codeinsult.egg-info/PKG-INFO +113 -0
- codeinsult-0.1.0/codeinsult.egg-info/SOURCES.txt +21 -0
- codeinsult-0.1.0/codeinsult.egg-info/dependency_links.txt +1 -0
- codeinsult-0.1.0/codeinsult.egg-info/requires.txt +4 -0
- codeinsult-0.1.0/codeinsult.egg-info/top_level.txt +1 -0
- codeinsult-0.1.0/docs/README.md +88 -0
- codeinsult-0.1.0/pyproject.toml +42 -0
- codeinsult-0.1.0/setup.cfg +4 -0
- codeinsult-0.1.0/tests/test_codeinsult.py +245 -0
- codeinsult-0.1.0/tests/test_config.py +72 -0
- codeinsult-0.1.0/tests/test_messages_base.py +164 -0
- codeinsult-0.1.0/tests/test_registry.py +92 -0
- codeinsult-0.1.0/tests/test_version.py +26 -0
codeinsult-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vaz15k
|
|
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.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codeinsult
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Turn HTTP errors into laughter — funny, sarcastic & offensive messages for every status code.
|
|
5
|
+
Author: Vaz15k
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Source, https://github.com/Vaz15k/code-insult
|
|
8
|
+
Project-URL: Tracker, https://github.com/Vaz15k/code-insult/issues
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest; extra == "dev"
|
|
23
|
+
Requires-Dist: ruff; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# CodeInsult
|
|
27
|
+
|
|
28
|
+
> **Turn errors into laughter.** A Python library that maps HTTP status codes to funny, sarcastic, and offensive messages — from "cult reference" to "heavy insult" levels.
|
|
29
|
+
|
|
30
|
+
> **English 🇺🇸** | [Português 🇧🇷](docs/README.pt.md)
|
|
31
|
+
|
|
32
|
+
[](https://www.python.org/)
|
|
33
|
+
[](LICENSE)
|
|
34
|
+
[](codeinsult/__version__.py)
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install codeinsult
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> *Requires Python 3.11 or higher.*
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import codeinsult
|
|
52
|
+
|
|
53
|
+
# Get a message for a status code
|
|
54
|
+
print(codeinsult.insult(404))
|
|
55
|
+
# "404: O recurso sumiu. Como meias na máquina de lavar."
|
|
56
|
+
|
|
57
|
+
# With severity level ("light", "medium", "heavy")
|
|
58
|
+
print(codeinsult.insult(500, level="heavy"))
|
|
59
|
+
# "DEU MERDA NO SERVIDOR! Não foi você, foi a gente. Mas foi você também."
|
|
60
|
+
|
|
61
|
+
# A completely random message
|
|
62
|
+
print(codeinsult.random_insult())
|
|
63
|
+
# "418 I'm a Teapot: Sou um bule, não uma cafeteira. Respeita minha profissão!"
|
|
64
|
+
|
|
65
|
+
# Configure global defaults
|
|
66
|
+
codeinsult.set_defaults(lang="pt_br", level="light")
|
|
67
|
+
print(codeinsult.insult(401))
|
|
68
|
+
# "401 Unauthorized: Você não vai passar! — Gandalf, Servidor Edition."
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Severity Levels
|
|
74
|
+
|
|
75
|
+
| Level | Description | Example |
|
|
76
|
+
| ---------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
|
|
77
|
+
| `LIGHT` | References to movies, series, books, and memes. No swearing. | *"404: Procurei em toda galáxia e não achei. — Baby Yoda."* |
|
|
78
|
+
| `MEDIUM` | Sarcasm and irony. Mildly offensive language. | *"500: O servidor teve um treco. Tenta de novo quando ele se recuperar."* |
|
|
79
|
+
| `HEAVY` | Heavy insults, with explicit swear words and profanity. | *"400: QUE REQUEST DE MERDA É ESSE? Nem o Google Tradutor salva essa bagunça!"* |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Development
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Clone the repository
|
|
87
|
+
git clone https://github.com/Vaz15k/code-insult.git
|
|
88
|
+
cd code-insult
|
|
89
|
+
|
|
90
|
+
# Install dev dependencies
|
|
91
|
+
pip install -e ".[dev]"
|
|
92
|
+
|
|
93
|
+
# Run the tests
|
|
94
|
+
pytest tests/ -v
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Contributing
|
|
100
|
+
|
|
101
|
+
Contributions are very welcome! Some ideas:
|
|
102
|
+
|
|
103
|
+
- 🗣️ **Add new languages** — Spanish, French, etc.
|
|
104
|
+
- 💬 **Add more phrases** for existing status codes (Be Creative)
|
|
105
|
+
- 🐛 **Fix bugs** or improve documentation
|
|
106
|
+
- 📦 **New status codes** that don't have messages yet
|
|
107
|
+
|
|
108
|
+
To contribute:
|
|
109
|
+
|
|
110
|
+
1. Fork the project
|
|
111
|
+
2. Create a branch for your feature
|
|
112
|
+
3. Add your messages and tests
|
|
113
|
+
4. Submit a Pull Request
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import random
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from codeinsult.config import InsultLevel
|
|
5
|
+
from codeinsult.messages.registry import available_languages, get_provider
|
|
6
|
+
|
|
7
|
+
_default_lang: str = "pt_br"
|
|
8
|
+
_default_level: Optional[InsultLevel] = None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def set_defaults(
|
|
12
|
+
*,
|
|
13
|
+
lang: str = "pt_br",
|
|
14
|
+
level: Optional[str] = None,
|
|
15
|
+
) -> None:
|
|
16
|
+
"""Configura o idioma e nível de severidade padrão globais.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
lang: Idioma padrão (ex: ``"pt_br"``).
|
|
20
|
+
level: Nível de severidade padrão (``"light"``, ``"medium"``,
|
|
21
|
+
``"heavy"``). ``None`` para usar todos os níveis.
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
>>> codeinsult.set_defaults(lang="pt_br", level="light")
|
|
25
|
+
>>> codeinsult.insult(404) # usa pt_br + LIGHT
|
|
26
|
+
"""
|
|
27
|
+
global _default_lang, _default_level
|
|
28
|
+
if lang not in available_languages():
|
|
29
|
+
raise ValueError(
|
|
30
|
+
f"Idioma '{lang}' não disponível. Disponíveis: {available_languages()}"
|
|
31
|
+
)
|
|
32
|
+
_default_lang = lang
|
|
33
|
+
_default_level = InsultLevel.from_str(level)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def insult(
|
|
37
|
+
status_code: int,
|
|
38
|
+
*,
|
|
39
|
+
level: Optional[str] = None,
|
|
40
|
+
lang: Optional[str] = None,
|
|
41
|
+
) -> str:
|
|
42
|
+
"""
|
|
43
|
+
Retorna uma mensagem engraçada para o código de status HTTP informado.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
status_code: Código de status HTTP (ex: 200, 404, 500).
|
|
47
|
+
level: Nível de severidade (``"light"``, ``"medium"``, ``"heavy"``).
|
|
48
|
+
Se None, usa TODOS os níveis.
|
|
49
|
+
lang: Idioma (ex: ``"pt_br"``). Se None, usa ``"pt_br"``.
|
|
50
|
+
"""
|
|
51
|
+
parsed_level = InsultLevel.from_str(level) if level is not None else _default_level
|
|
52
|
+
lang = lang if lang is not None else _default_lang
|
|
53
|
+
|
|
54
|
+
provider = get_provider(lang)
|
|
55
|
+
messages = provider.get_messages(status_code, parsed_level)
|
|
56
|
+
|
|
57
|
+
if not messages:
|
|
58
|
+
if parsed_level is None:
|
|
59
|
+
messages = []
|
|
60
|
+
for lvl in InsultLevel:
|
|
61
|
+
messages.extend(provider.catalog.default_messages.get(lvl, []))
|
|
62
|
+
else:
|
|
63
|
+
messages = provider.catalog.default_messages.get(
|
|
64
|
+
parsed_level, ["Erro desconhecido."]
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if not messages:
|
|
68
|
+
return f"Status {status_code}: sem mensagem disponível."
|
|
69
|
+
return random.choice(messages)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def random_insult(
|
|
73
|
+
*,
|
|
74
|
+
level: Optional[str] = None,
|
|
75
|
+
lang: Optional[str] = None,
|
|
76
|
+
) -> str:
|
|
77
|
+
"""
|
|
78
|
+
Retorna uma mensagem aleatória de qualquer status code disponível.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
level: Nível de severidade (``"light"``, ``"medium"``, ``"heavy"``).
|
|
82
|
+
Se None, usa TODOS os níveis.
|
|
83
|
+
lang: Idioma (ex: ``"pt_br"``). Se None, usa ``"pt_br"``.
|
|
84
|
+
"""
|
|
85
|
+
parsed_level = InsultLevel.from_str(level) if level is not None else _default_level
|
|
86
|
+
lang = lang if lang is not None else _default_lang
|
|
87
|
+
|
|
88
|
+
provider = get_provider(lang)
|
|
89
|
+
catalog = provider.catalog
|
|
90
|
+
|
|
91
|
+
# Coleta todas as mensagens
|
|
92
|
+
all_msgs: list[str] = []
|
|
93
|
+
if parsed_level is None:
|
|
94
|
+
for code_msgs in catalog.messages.values():
|
|
95
|
+
for lvl in InsultLevel:
|
|
96
|
+
all_msgs.extend(code_msgs.get(lvl, []))
|
|
97
|
+
else:
|
|
98
|
+
for code_msgs in catalog.messages.values():
|
|
99
|
+
all_msgs.extend(code_msgs.get(parsed_level, []))
|
|
100
|
+
|
|
101
|
+
if not all_msgs:
|
|
102
|
+
if parsed_level is None:
|
|
103
|
+
for lvl in InsultLevel:
|
|
104
|
+
all_msgs.extend(catalog.default_messages.get(lvl, []))
|
|
105
|
+
else:
|
|
106
|
+
all_msgs = catalog.default_messages.get(parsed_level, ["Nada a declarar."])
|
|
107
|
+
return random.choice(all_msgs)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# codeinsult/__init__.py
|
|
2
|
+
import codeinsult.messages.pt_br # noqa: F401, F811
|
|
3
|
+
from codeinsult.__version__ import __version__
|
|
4
|
+
from codeinsult.CodeInsult import insult, random_insult, set_defaults
|
|
5
|
+
from codeinsult.messages.registry import available_languages
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"__version__",
|
|
9
|
+
"insult",
|
|
10
|
+
"random_insult",
|
|
11
|
+
"set_defaults",
|
|
12
|
+
"available_languages",
|
|
13
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class InsultLevel(Enum):
|
|
6
|
+
"""
|
|
7
|
+
Níveis de severidade das mensagens.
|
|
8
|
+
|
|
9
|
+
LIGHT — Referências leves a filmes, séries, livros, memes. Sem palavrões.
|
|
10
|
+
MEDIUM — Sarcasmo e ironia. Pode conter linguagem levemente ofensiva.
|
|
11
|
+
HEAVY — Insultos pesados, com palavrões e xingamentos explícitos.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
LIGHT = "light"
|
|
15
|
+
MEDIUM = "medium"
|
|
16
|
+
HEAVY = "heavy"
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def from_str(cls, level: Optional[str]) -> Optional["InsultLevel"]:
|
|
20
|
+
"""
|
|
21
|
+
Converte uma string para ``InsultLevel``.
|
|
22
|
+
|
|
23
|
+
Aceita ``"light"``, ``"medium"``, ``"heavy"``.
|
|
24
|
+
``None`` retorna ``None``.
|
|
25
|
+
"""
|
|
26
|
+
if level is None:
|
|
27
|
+
return None
|
|
28
|
+
try:
|
|
29
|
+
return cls(level.lower())
|
|
30
|
+
except ValueError:
|
|
31
|
+
raise ValueError(
|
|
32
|
+
f"Nível '{level}' inválido. Use: light, medium, heavy."
|
|
33
|
+
) from None
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from codeinsult.config import InsultLevel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class MessageCatalog:
|
|
10
|
+
"""
|
|
11
|
+
Catálogo de mensagens para um idioma.
|
|
12
|
+
|
|
13
|
+
Estrutura:
|
|
14
|
+
messages[status_code][InsultLevel] = [lista de frases]
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
lang: str
|
|
18
|
+
lang_name: str
|
|
19
|
+
messages: Dict[int, Dict[InsultLevel, List[str]]] = field(default_factory=dict)
|
|
20
|
+
default_messages: Dict[InsultLevel, List[str]] = field(default_factory=dict)
|
|
21
|
+
|
|
22
|
+
def get_messages(self, status_code: int, level: Optional[InsultLevel]) -> List[str]:
|
|
23
|
+
"""
|
|
24
|
+
Retorna as mensagens para um status code e nível.
|
|
25
|
+
Se level for None, retorna mensagens de TODOS os níveis.
|
|
26
|
+
Se não houver mensagens específicas, retorna as padrão.
|
|
27
|
+
"""
|
|
28
|
+
if level is None:
|
|
29
|
+
# Coleta mensagens de todos os níveis
|
|
30
|
+
code_msgs = self.messages.get(status_code, {})
|
|
31
|
+
msgs: List[str] = []
|
|
32
|
+
for lvl in InsultLevel:
|
|
33
|
+
msgs.extend(code_msgs.get(lvl, []))
|
|
34
|
+
if not msgs:
|
|
35
|
+
for lvl in InsultLevel:
|
|
36
|
+
msgs.extend(self.default_messages.get(lvl, []))
|
|
37
|
+
return msgs
|
|
38
|
+
|
|
39
|
+
code_msgs = self.messages.get(status_code, {})
|
|
40
|
+
msgs = code_msgs.get(level, [])
|
|
41
|
+
if not msgs:
|
|
42
|
+
msgs = self.default_messages.get(level, [])
|
|
43
|
+
return msgs
|
|
44
|
+
|
|
45
|
+
def has_messages(self, status_code: int, level: Optional[InsultLevel]) -> bool:
|
|
46
|
+
"""Verifica se existem mensagens para o status e nível."""
|
|
47
|
+
if level is None:
|
|
48
|
+
code_msgs = self.messages.get(status_code, {})
|
|
49
|
+
return any(code_msgs.get(lvl, []) for lvl in InsultLevel)
|
|
50
|
+
return bool(self.messages.get(status_code, {}).get(level, []))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class BaseMessageProvider(ABC):
|
|
54
|
+
"""
|
|
55
|
+
Provider abstrato — cada idioma implementa um provider concreto.
|
|
56
|
+
|
|
57
|
+
Para adicionar um novo idioma:
|
|
58
|
+
|
|
59
|
+
class EnUSProvider(BaseMessageProvider):
|
|
60
|
+
@property
|
|
61
|
+
def catalog(self) -> MessageCatalog:
|
|
62
|
+
return _EN_US_CATALOG
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def catalog(self) -> MessageCatalog:
|
|
68
|
+
"""Retorna o catálogo de mensagens para este idioma."""
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def lang_code(cls) -> str:
|
|
74
|
+
"""Código do idioma (ex: 'pt_BR', 'en_US')."""
|
|
75
|
+
...
|
|
76
|
+
|
|
77
|
+
def get_messages(self, status_code: int, level: Optional[InsultLevel]) -> List[str]:
|
|
78
|
+
return self.catalog.get_messages(status_code, level)
|