• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

angelkurten / response_handler / 14917348890

08 May 2025 10:28PM UTC coverage: 98.986% (-0.3%) from 99.299%
14917348890

push

github

web-flow
feat: Add Success.of() factory method and improve JSON output control (#13)

* ci: add support for Python 3.10, 3.11, and 3.12

* feat: add Success.of() factory method and improve JSON output control

* test: update tests for new JSON output control features

* chore: bump version to 0.2.1

* docs: update documentation and examples with latest features

* feat: add success data type

44 of 47 new or added lines in 4 files covered. (93.62%)

1 existing line in 1 file now uncovered.

1172 of 1184 relevant lines covered (98.99%)

0.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.05
/response_handler_lib/either.py
1
from dataclasses import dataclass, asdict
1✔
2
from typing import TypeVar, Generic, Optional, List, Dict, Any, Union, Type, cast
1✔
3
import json
1✔
4
import os
1✔
5
import inspect
1✔
6

7
from response_handler_lib.error_codes import PredefinedErrorCodes
1✔
8
from response_handler_lib.config import Config
1✔
9
from response_handler_lib.types import SuccessData
1✔
10

11
L = TypeVar('L')  # Tipo para el error
1✔
12
R = TypeVar('R')  # Tipo para el éxito
1✔
13
T = TypeVar('T')  # Tipo para el valor de éxito en el método of
1✔
14

15

16
@dataclass
1✔
17
class ErrorItem:
1✔
18
    code: str
1✔
19
    message: str
1✔
20
    where: Optional[str] = None
1✔
21
    context: Optional[Dict[str, Any]] = None
1✔
22

23
    @classmethod
1✔
24
    def create(cls, code: Union[str, PredefinedErrorCodes], message: Optional[str] = None, context: Optional[Dict[str, Any]] = None) -> 'ErrorItem':
1✔
25
        frame = inspect.currentframe().f_back
1✔
26
        filename = os.path.basename(frame.f_globals["__file__"])
1✔
27
        line_number = frame.f_lineno
1✔
28
        class_name = frame.f_locals.get("self", None).__class__.__name__ if "self" in frame.f_locals else None
1✔
29
        method_name = frame.f_code.co_name
1✔
30

31
        location = f"{filename}, {class_name}.{method_name}, line {line_number}" \
1✔
32
            if class_name \
33
            else \
34
            f"{filename}, {method_name}, line {line_number}"
35

36
        if isinstance(code, PredefinedErrorCodes):
1✔
37
            error_code = code.value
1✔
38
            error_message = message or code.name.replace('_', ' ').title()
1✔
39
        else:
40
            error_code = code
1✔
41
            error_message = message or "An error occurred"
1✔
42

43
        error = cls(code=error_code, message=error_message, where=location, context=context)
1✔
44
        
45
        if Config.ENABLE_LOGS:
1✔
46
            Config.LOGGER.error(
1✔
47
                f"Error created:\n"
48
                f"  Code: {error.code}\n"
49
                f"  Message: {error.message}\n"
50
                f"  Location: {error.where}\n"
51
                f"  Context: {error.context if error.context else 'None'}"
52
            )
53
            
54
        return error
1✔
55

56
    def to_dict(self, include_where: bool = False) -> Dict[str, Any]:
1✔
57
        result = {
1✔
58
            "code": self.code,
59
            "message": self.message
60
        }
61
        if include_where and self.where:
1✔
62
            result["where"] = self.where
1✔
63
        if Config.ENABLE_CONTEXT_IN_JSON and self.context:
1✔
64
            result["context"] = self.context
1✔
65
        return result
1✔
66

67

68
class Either(Generic[L, R]):
1✔
69
    @staticmethod
1✔
70
    def success(value: R) -> 'Success[L, R]':
1✔
71
        return Success(value)
×
72

73
    @staticmethod
1✔
74
    def failure(error: Union[ErrorItem, List[ErrorItem]]) -> 'Failure[L, R]':
1✔
75
        return Failure(error)
×
76

77

78
class Success(Either[L, R]):
1✔
79
    def __init__(self, value: R):
1✔
80
        self._value = value
1✔
81

82
    @classmethod
1✔
83
    def of(cls, value: T) -> 'Success[L, T]':
1✔
84
        """
85
        Método de fábrica que permite crear un Success con un tipo específico.
86
        Útil cuando se quiere especificar explícitamente el tipo genérico.
87
        """
NEW
88
        return cast(Success[L, T], cls(value))
×
89

90
    @property
1✔
91
    def is_right(self) -> bool:
1✔
92
        return True
1✔
93

94
    @property
1✔
95
    def is_left(self) -> bool:
1✔
96
        return False
1✔
97

98
    def get_right(self) -> R:
1✔
99
        return self._value
1✔
100

101
    def get_left(self) -> None:
1✔
102
        return None
1✔
103

104
    def map(self, f) -> 'Success[L, R]':
1✔
105
        return Success(f(self._value))
1✔
106

107
    def flat_map(self, f) -> Either[L, R]:
1✔
108
        return f(self._value)
1✔
109

110
    def to_json(self) -> str:
1✔
111
        if isinstance(self._value, SuccessData):
1✔
NEW
112
            return json.dumps(self._value.model_dump())
×
113
        return json.dumps({"data": self._value})
1✔
114

115
    def to_dict(self) -> Dict[str, Any]:
1✔
116
        if isinstance(self._value, SuccessData):
1✔
NEW
117
            return self._value.model_dump()
×
118
        return {"data": self._value}
1✔
119

120

121
class Failure(Either[L, R]):
1✔
122
    def __init__(self, error: Union[ErrorItem, List[ErrorItem]]):
1✔
123
        if isinstance(error, ErrorItem):
1✔
124
            self._errors = [error]
1✔
125
        elif isinstance(error, list):
1✔
126
            self._errors = error
1✔
127
        else:
128
            raise ValueError("Error must be an ErrorItem or list of ErrorItem")
1✔
129

130
    @property
1✔
131
    def is_right(self) -> bool:
1✔
132
        return False
1✔
133

134
    @property
1✔
135
    def is_left(self) -> bool:
1✔
136
        return True
1✔
137

138
    def get_right(self) -> None:
1✔
139
        return None
1✔
140

141
    def get_left(self) -> List[ErrorItem]:
1✔
142
        return self._errors
1✔
143

144
    def map(self, f) -> 'Failure[L, R]':
1✔
145
        return self
1✔
146

147
    def flat_map(self, f) -> 'Failure[L, R]':
1✔
148
        return self
1✔
149

150
    def to_json(self, include_where: bool = False) -> str:
1✔
151
        return json.dumps({"errors": [error.to_dict(include_where) for error in self._errors]})
1✔
152

153
    def to_dict(self, include_where: bool = False) -> Dict[str, Any]:
1✔
154
        return {"errors": [error.to_dict(include_where) for error in self._errors]} 
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc