shwary-python 2.0.0__tar.gz → 2.0.2__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.
- {shwary_python-2.0.0 → shwary_python-2.0.2}/PKG-INFO +7 -6
- {shwary_python-2.0.0 → shwary_python-2.0.2}/README.md +5 -4
- {shwary_python-2.0.0 → shwary_python-2.0.2}/pyproject.toml +6 -3
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/clients/async_client.py +5 -23
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/clients/sync.py +5 -20
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/core.py +21 -1
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/schemas.py +5 -2
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/tests/test_client_async.py +14 -3
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/tests/test_schemas.py +4 -6
- shwary_python-2.0.0/shwary/logger.py +0 -32
- {shwary_python-2.0.0 → shwary_python-2.0.2}/.gitignore +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2}/LICENSE +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/__init__.py +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/__version__.py +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/clients/__init__.py +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/clients/base.py +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/exceptions.py +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/logging_config.py +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/py.typed +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/tests/__init__.py +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/tests/test_advanced.py +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/tests/test_client.py +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/tests/test_validators.py +0 -0
- {shwary_python-2.0.0 → shwary_python-2.0.2/src}/shwary/validators.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shwary-python
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: SDK Python moderne (Async/Sync) pour l'API de paiement Shwary.
|
|
5
5
|
Author-email: Josué Luis Panzu <josuepanzu8@gmail.com>
|
|
6
6
|
License: MIT
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Keywords: africa,fintech,kenya,mobile-money,payment,rdc,shwary,uganda
|
|
9
|
-
Classifier: Development Status ::
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -24,9 +24,9 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
[](https://pypi.org/project/shwary-python/)
|
|
25
25
|
[](https://opensource.org/licenses/MIT)
|
|
26
26
|
|
|
27
|
-
**Shwary Python** est une bibliothèque cliente moderne, asynchrone et performante
|
|
27
|
+
**Shwary Python** est une bibliothèque cliente moderne, asynchrone et performante pour l'intégration de l'API [Shwary](https://shwary.com). Elle permet d'initier des paiements Mobile Money en **RDC**, au **Kenya** et en **Ouganda** avec une validation stricte des données avant l'envoi.
|
|
28
28
|
|
|
29
|
-
## Quoi de neuf dans la v2.0.
|
|
29
|
+
## Quoi de neuf dans la v2.0.2
|
|
30
30
|
|
|
31
31
|
- **Retry automatique** : Les erreurs réseau transitoires (timeout, connexion) sont automatiquement retentées avec backoff exponentiel
|
|
32
32
|
- **Types stricts** : TypedDict pour les réponses (`PaymentResponse`, `TransactionResponse`, `WebhookPayload`)
|
|
@@ -36,6 +36,7 @@ Description-Content-Type: text/markdown
|
|
|
36
36
|
- **Docstrings améliorées** : Documentation complète avec exemples d'utilisation
|
|
37
37
|
- **Tests étendus** : Couverture complète des retries, erreurs et validations
|
|
38
38
|
- **Modèles de réponse** : Schemas Pydantic pour les webhooks et transactions
|
|
39
|
+
- **Correction des bugs** : Correction des imports et des bugs mineurs
|
|
39
40
|
|
|
40
41
|
## Caractéristiques
|
|
41
42
|
|
|
@@ -170,12 +171,12 @@ async def handle_webhook(payload: WebhookPayload):
|
|
|
170
171
|
"""Shwary envoie une notification de changement d'état."""
|
|
171
172
|
|
|
172
173
|
if payload.status == "completed":
|
|
173
|
-
#
|
|
174
|
+
# Transaction réussie
|
|
174
175
|
print(f"Paiement {payload.id} reçu ({payload.amount})")
|
|
175
176
|
# La livrez le service ici
|
|
176
177
|
|
|
177
178
|
elif payload.status == "failed":
|
|
178
|
-
#
|
|
179
|
+
# Transaction échouée
|
|
179
180
|
print(f"Paiement {payload.id} échoué")
|
|
180
181
|
# Notifiez le client
|
|
181
182
|
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
[](https://pypi.org/project/shwary-python/)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
|
|
7
|
-
**Shwary Python** est une bibliothèque cliente moderne, asynchrone et performante
|
|
7
|
+
**Shwary Python** est une bibliothèque cliente moderne, asynchrone et performante pour l'intégration de l'API [Shwary](https://shwary.com). Elle permet d'initier des paiements Mobile Money en **RDC**, au **Kenya** et en **Ouganda** avec une validation stricte des données avant l'envoi.
|
|
8
8
|
|
|
9
|
-
## Quoi de neuf dans la v2.0.
|
|
9
|
+
## Quoi de neuf dans la v2.0.2
|
|
10
10
|
|
|
11
11
|
- **Retry automatique** : Les erreurs réseau transitoires (timeout, connexion) sont automatiquement retentées avec backoff exponentiel
|
|
12
12
|
- **Types stricts** : TypedDict pour les réponses (`PaymentResponse`, `TransactionResponse`, `WebhookPayload`)
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
- **Docstrings améliorées** : Documentation complète avec exemples d'utilisation
|
|
17
17
|
- **Tests étendus** : Couverture complète des retries, erreurs et validations
|
|
18
18
|
- **Modèles de réponse** : Schemas Pydantic pour les webhooks et transactions
|
|
19
|
+
- **Correction des bugs** : Correction des imports et des bugs mineurs
|
|
19
20
|
|
|
20
21
|
## Caractéristiques
|
|
21
22
|
|
|
@@ -150,12 +151,12 @@ async def handle_webhook(payload: WebhookPayload):
|
|
|
150
151
|
"""Shwary envoie une notification de changement d'état."""
|
|
151
152
|
|
|
152
153
|
if payload.status == "completed":
|
|
153
|
-
#
|
|
154
|
+
# Transaction réussie
|
|
154
155
|
print(f"Paiement {payload.id} reçu ({payload.amount})")
|
|
155
156
|
# La livrez le service ici
|
|
156
157
|
|
|
157
158
|
elif payload.status == "failed":
|
|
158
|
-
#
|
|
159
|
+
# Transaction échouée
|
|
159
160
|
print(f"Paiement {payload.id} échoué")
|
|
160
161
|
# Notifiez le client
|
|
161
162
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "shwary-python"
|
|
3
|
-
version = "2.0.
|
|
3
|
+
version = "2.0.2"
|
|
4
4
|
description = "SDK Python moderne (Async/Sync) pour l'API de paiement Shwary."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
"License :: OSI Approved :: MIT License",
|
|
21
21
|
"Operating System :: OS Independent",
|
|
22
22
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
|
-
"Development Status ::
|
|
23
|
+
"Development Status :: 5 - Production/Stable",
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
[build-system]
|
|
@@ -45,7 +45,10 @@ line-length = 88
|
|
|
45
45
|
select = ["E", "F", "I"] # Erreurs, Flakes, et tris d'Imports
|
|
46
46
|
|
|
47
47
|
[tool.hatch.build.targets.sdist]
|
|
48
|
-
|
|
48
|
+
only-include = ["src/shwary"]
|
|
49
49
|
|
|
50
50
|
[tool.hatch.build.targets.wheel]
|
|
51
51
|
packages = ["src/shwary"]
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel.sources]
|
|
54
|
+
"src" = ""
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
1
|
import httpx
|
|
4
|
-
from tenacity import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
stop_after_attempt,
|
|
8
|
-
wait_exponential,
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
from ..core import prepare_payment_request
|
|
2
|
+
from tenacity import AsyncRetrying
|
|
3
|
+
|
|
4
|
+
from ..core import get_retrying_options, prepare_payment_request
|
|
12
5
|
from ..exceptions import raise_from_response
|
|
13
6
|
from ..schemas import PaymentResponse, TransactionResponse
|
|
14
7
|
from .base import BaseShwaryClient
|
|
@@ -76,17 +69,6 @@ class ShwaryAsync(BaseShwaryClient):
|
|
|
76
69
|
"""Sortie du context manager asynchrone."""
|
|
77
70
|
await self.close()
|
|
78
71
|
|
|
79
|
-
@property
|
|
80
|
-
def _retrying_options(self) -> dict[str, Any]:
|
|
81
|
-
return {
|
|
82
|
-
"retry": retry_if_exception_type(
|
|
83
|
-
(httpx.TimeoutException, httpx.ConnectError)
|
|
84
|
-
),
|
|
85
|
-
"stop": stop_after_attempt(3),
|
|
86
|
-
"wait": wait_exponential(multiplier=1, min=2, max=10),
|
|
87
|
-
"reraise": True,
|
|
88
|
-
}
|
|
89
|
-
|
|
90
72
|
async def initiate_payment(
|
|
91
73
|
self,
|
|
92
74
|
country: str,
|
|
@@ -129,7 +111,7 @@ class ShwaryAsync(BaseShwaryClient):
|
|
|
129
111
|
|
|
130
112
|
self._log_request(endpoint, json_data)
|
|
131
113
|
|
|
132
|
-
async for attempt in AsyncRetrying(**
|
|
114
|
+
async for attempt in AsyncRetrying(**get_retrying_options()):
|
|
133
115
|
with attempt:
|
|
134
116
|
try:
|
|
135
117
|
response = await self._client.post(endpoint, json=json_data)
|
|
@@ -166,7 +148,7 @@ class ShwaryAsync(BaseShwaryClient):
|
|
|
166
148
|
|
|
167
149
|
self.logger.debug(f"Fetching transaction: {transaction_id}")
|
|
168
150
|
|
|
169
|
-
async for attempt in AsyncRetrying(**
|
|
151
|
+
async for attempt in AsyncRetrying(**get_retrying_options()):
|
|
170
152
|
with attempt:
|
|
171
153
|
try:
|
|
172
154
|
response = await self._client.get(endpoint)
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import httpx
|
|
2
|
-
from tenacity import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
stop_after_attempt,
|
|
6
|
-
wait_exponential,
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
from ..core import prepare_payment_request
|
|
2
|
+
from tenacity import retry
|
|
3
|
+
|
|
4
|
+
from ..core import get_retrying_options, prepare_payment_request
|
|
10
5
|
from ..exceptions import raise_from_response
|
|
11
6
|
from ..schemas import PaymentResponse, TransactionResponse
|
|
12
7
|
from .base import BaseShwaryClient
|
|
@@ -74,12 +69,7 @@ class Shwary(BaseShwaryClient):
|
|
|
74
69
|
"""Sortie du context manager."""
|
|
75
70
|
self.close()
|
|
76
71
|
|
|
77
|
-
@retry(
|
|
78
|
-
retry=retry_if_exception_type((httpx.TimeoutException, httpx.ConnectError)),
|
|
79
|
-
stop=stop_after_attempt(3),
|
|
80
|
-
wait=wait_exponential(multiplier=1, min=2, max=10),
|
|
81
|
-
reraise=True,
|
|
82
|
-
)
|
|
72
|
+
@retry(**get_retrying_options())
|
|
83
73
|
def initiate_payment(
|
|
84
74
|
self,
|
|
85
75
|
country: str,
|
|
@@ -133,12 +123,7 @@ class Shwary(BaseShwaryClient):
|
|
|
133
123
|
self._log_error(endpoint, e)
|
|
134
124
|
raise
|
|
135
125
|
|
|
136
|
-
@retry(
|
|
137
|
-
retry=retry_if_exception_type((httpx.TimeoutException, httpx.ConnectError)),
|
|
138
|
-
stop=stop_after_attempt(3),
|
|
139
|
-
wait=wait_exponential(multiplier=1, min=2, max=10),
|
|
140
|
-
reraise=True,
|
|
141
|
-
)
|
|
126
|
+
@retry(**get_retrying_options())
|
|
142
127
|
def get_transaction(self, transaction_id: str) -> TransactionResponse:
|
|
143
128
|
"""
|
|
144
129
|
Récupère les détails d'une transaction par son ID.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Logique métier partagée entre les clients Sync et Async.
|
|
3
3
|
|
|
4
|
-
Ce module contient les fonctions de validation et de préparation des requêtes
|
|
4
|
+
Ce module contient notamment les fonctions de validation et de préparation des requêtes
|
|
5
5
|
utilisées par tous les clients Shwary pour éviter la duplication de code.
|
|
6
6
|
|
|
7
7
|
Fonctions principales:
|
|
@@ -20,6 +20,15 @@ Exemple d'utilisation directe:
|
|
|
20
20
|
# payload = {"amount": 5000, "clientPhoneNumber": "+243972345678"}
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
import httpx
|
|
26
|
+
from tenacity import (
|
|
27
|
+
retry_if_exception_type,
|
|
28
|
+
stop_after_attempt,
|
|
29
|
+
wait_exponential,
|
|
30
|
+
)
|
|
31
|
+
|
|
23
32
|
from .exceptions import ValidationError
|
|
24
33
|
from .schemas import CountryCode, PaymentPayload
|
|
25
34
|
|
|
@@ -166,3 +175,14 @@ def prepare_payment_request(
|
|
|
166
175
|
endpoint: str = f"/payment/{env_path}{country_enum.value}"
|
|
167
176
|
|
|
168
177
|
return endpoint, payload.model_dump(exclude={'country_target'}, exclude_none=True)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def get_retrying_options() -> dict[str, Any]:
|
|
181
|
+
return {
|
|
182
|
+
"retry": retry_if_exception_type(
|
|
183
|
+
(httpx.TimeoutException, httpx.ConnectError)
|
|
184
|
+
),
|
|
185
|
+
"stop": stop_after_attempt(3),
|
|
186
|
+
"wait": wait_exponential(multiplier=1, min=2, max=10),
|
|
187
|
+
"reraise": True,
|
|
188
|
+
}
|
|
@@ -122,6 +122,9 @@ class PaymentResponse(BaseModel):
|
|
|
122
122
|
|
|
123
123
|
id: str = Field(..., description="Identifiant unique de la transaction (UUID)")
|
|
124
124
|
status: str = Field(..., description="État de la transaction")
|
|
125
|
+
isSandbox: bool = Field(
|
|
126
|
+
..., description="Indique si la transaction est en mode sandbox"
|
|
127
|
+
)
|
|
125
128
|
|
|
126
129
|
model_config = {"extra": "allow"}
|
|
127
130
|
|
|
@@ -134,7 +137,7 @@ class TransactionResponse(BaseModel):
|
|
|
134
137
|
id: Identifiant unique de la transaction
|
|
135
138
|
status: État actualisé de la transaction
|
|
136
139
|
amount: Montant de la transaction
|
|
137
|
-
|
|
140
|
+
recipientPhoneNumber: Numéro du receveur
|
|
138
141
|
createdAt: Timestamp de création
|
|
139
142
|
updatedAt: Timestamp de dernière mise à jour
|
|
140
143
|
metadata: Données métier additionnelles
|
|
@@ -145,7 +148,7 @@ class TransactionResponse(BaseModel):
|
|
|
145
148
|
..., description="État actualisé (pending, completed, failed, etc.)"
|
|
146
149
|
)
|
|
147
150
|
amount: float = Field(..., description="Montant de la transaction")
|
|
148
|
-
|
|
151
|
+
recipientPhoneNumber: Optional[str] = Field(None, description="Numéro du receveur")
|
|
149
152
|
createdAt: Optional[datetime] = Field(None, description="Timestamp de création")
|
|
150
153
|
updatedAt: Optional[datetime] = Field(
|
|
151
154
|
None, description="Timestamp de dernière mise à jour"
|
|
@@ -146,18 +146,29 @@ async def test_async_retry_on_timeout(async_client):
|
|
|
146
146
|
async def test_async_parallel_payments(async_client):
|
|
147
147
|
"""Teste la capacité à gérer plusieurs requêtes simultanées."""
|
|
148
148
|
respx.post("https://api.shwary.com/api/v1/merchants/payment/sandbox/DRC").mock(
|
|
149
|
-
return_value=Response(
|
|
149
|
+
return_value=Response(
|
|
150
|
+
200, json={"id": "p1", "status": "pending", "isSandbox": True}
|
|
151
|
+
)
|
|
150
152
|
)
|
|
151
153
|
respx.post("https://api.shwary.com/api/v1/merchants/payment/sandbox/KE").mock(
|
|
152
|
-
return_value=Response(
|
|
154
|
+
return_value=Response(
|
|
155
|
+
200, json={"id": "p2", "status": "pending", "isSandbox": True}
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
respx.post("https://api.shwary.com/api/v1/merchants/payment/sandbox/UG").mock(
|
|
159
|
+
return_value=Response(
|
|
160
|
+
200, json={"id": "p3", "status": "pending", "isSandbox": True}
|
|
161
|
+
)
|
|
153
162
|
)
|
|
154
163
|
|
|
155
164
|
async with async_client as c:
|
|
156
|
-
# On lance les
|
|
165
|
+
# On lance les trois en même temps
|
|
157
166
|
results = await asyncio.gather(
|
|
158
167
|
c.initiate_payment("DRC", 5000, "+243972345678"),
|
|
159
168
|
c.initiate_payment("KE", 10, "+254700000000"),
|
|
169
|
+
c.initiate_payment("UG", 50, "+256700000000"),
|
|
160
170
|
)
|
|
161
171
|
|
|
162
172
|
assert results[0].id == "p1"
|
|
163
173
|
assert results[1].id == "p2"
|
|
174
|
+
assert results[2].id == "p3"
|
|
@@ -144,7 +144,7 @@ class TestPaymentResponse:
|
|
|
144
144
|
id="trans-123",
|
|
145
145
|
status="pending",
|
|
146
146
|
isSandbox=True,
|
|
147
|
-
extra_field="extra_value",
|
|
147
|
+
extra_field="extra_value",
|
|
148
148
|
)
|
|
149
149
|
|
|
150
150
|
assert response.id == "trans-123"
|
|
@@ -160,7 +160,7 @@ class TestTransactionResponse:
|
|
|
160
160
|
id="trans-456",
|
|
161
161
|
status="completed",
|
|
162
162
|
amount=5000,
|
|
163
|
-
|
|
163
|
+
recipientPhoneNumber="+243972345678",
|
|
164
164
|
)
|
|
165
165
|
|
|
166
166
|
assert response.id == "trans-456"
|
|
@@ -174,10 +174,10 @@ class TestTransactionResponse:
|
|
|
174
174
|
status="pending",
|
|
175
175
|
amount=1000,
|
|
176
176
|
# Les champs suivants sont optionnels:
|
|
177
|
-
#
|
|
177
|
+
# recipientPhoneNumber, createdAt, updatedAt, metadata
|
|
178
178
|
)
|
|
179
179
|
|
|
180
|
-
assert response.
|
|
180
|
+
assert response.recipientPhoneNumber is None
|
|
181
181
|
|
|
182
182
|
|
|
183
183
|
class TestWebhookPayload:
|
|
@@ -185,12 +185,10 @@ class TestWebhookPayload:
|
|
|
185
185
|
|
|
186
186
|
def test_valid_webhook_payload(self):
|
|
187
187
|
"""Teste la création d'une charge webhook valide."""
|
|
188
|
-
now = datetime.now()
|
|
189
188
|
payload = WebhookPayload(
|
|
190
189
|
id="trans-webhook",
|
|
191
190
|
status="completed",
|
|
192
191
|
amount=5000,
|
|
193
|
-
timestamp=now,
|
|
194
192
|
)
|
|
195
193
|
|
|
196
194
|
assert payload.id == "trans-webhook"
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import sys
|
|
3
|
-
|
|
4
|
-
# Create a custom logger
|
|
5
|
-
logger = logging.getLogger(__name__)
|
|
6
|
-
|
|
7
|
-
# Create handlers
|
|
8
|
-
console_handler = logging.StreamHandler(sys.stdout)
|
|
9
|
-
file_handler = logging.FileHandler('app.log')
|
|
10
|
-
|
|
11
|
-
# Create formatters and add them to the handlers
|
|
12
|
-
console_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
|
|
13
|
-
file_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
14
|
-
console_handler.setFormatter(console_format)
|
|
15
|
-
file_handler.setFormatter(file_format)
|
|
16
|
-
|
|
17
|
-
# Set level for handlers
|
|
18
|
-
console_handler.setLevel(logging.DEBUG)
|
|
19
|
-
file_handler.setLevel(logging.ERROR)
|
|
20
|
-
|
|
21
|
-
# Add the handlers to the logger
|
|
22
|
-
logger.addHandler(console_handler)
|
|
23
|
-
logger.addHandler(file_handler)
|
|
24
|
-
|
|
25
|
-
# Set the default logging level
|
|
26
|
-
def set_log_level(level):
|
|
27
|
-
logger.setLevel(level)
|
|
28
|
-
|
|
29
|
-
# Example usage
|
|
30
|
-
if __name__ == '__main__':
|
|
31
|
-
logger.info('This is an info message')
|
|
32
|
-
logger.error('This is an error message')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|