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.
@@ -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
+ [![Python](https://img.shields.io/badge/python-≥3.11-blue.svg)](https://www.python.org/)
33
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
34
+ [![Version](https://img.shields.io/badge/version-0.1.0-orange.svg)](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,4 @@
1
+ # codeinsult/__version__.py
2
+ # coding: utf-8
3
+
4
+ __version__ = "0.1.0"
@@ -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,4 @@
1
+ # codeinsult/messages/__init__.py
2
+ from codeinsult.messages.base import BaseMessageProvider, MessageCatalog
3
+
4
+ __all__ = ["BaseMessageProvider", "MessageCatalog"]
@@ -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)