mon-package-python 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) 2024 Votre Nom
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,153 @@
1
+ Metadata-Version: 2.4
2
+ Name: mon-package-python
3
+ Version: 0.1.0
4
+ Summary: Un package Python professionnel créé avec Poetry
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: python,package,example
8
+ Author: Votre Nom
9
+ Author-email: votre.email@example.com
10
+ Requires-Python: >=3.10,<4.0
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Project-URL: Homepage, https://github.com/username/mon-package-python
21
+ Project-URL: Repository, https://github.com/username/mon-package-python
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Mon Package Python
25
+
26
+ [![Tests](https://github.com/username/mon-package-python/actions/workflows/tests.yaml/badge.svg)](https://github.com/username/mon-package-python/actions/workflows/tests.yaml)
27
+ [![Documentation](https://github.com/username/mon-package-python/actions/workflows/sphinx_doc.yaml/badge.svg)](https://username.github.io/mon-package-python/)
28
+ [![PyPI version](https://badge.fury.io/py/mon-package-python.svg)](https://badge.fury.io/py/mon-package-python)
29
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
30
+
31
+ Un package Python professionnel avec toutes les meilleures pratiques de développement.
32
+
33
+ ## 🚀 Installation
34
+
35
+ ```bash
36
+ pip install mon-package-python
37
+ ```
38
+
39
+ Ou pour un environnement de développement :
40
+
41
+ ```bash
42
+ git clone https://github.com/username/mon-package-python.git
43
+ cd mon-package-python
44
+ poetry install
45
+ ```
46
+
47
+ ## 📖 Utilisation
48
+
49
+ ```python
50
+ from mon_package_python.string_ops import reverse_string, count_vowels
51
+
52
+ # Inverser une chaîne
53
+ result = reverse_string("Hello World")
54
+ print(result) # "dlroW olleH"
55
+
56
+ # Compter les voyelles
57
+ vowels = count_vowels("Hello World")
58
+ print(vowels) # 3
59
+ ```
60
+
61
+ ## 🛠️ Fonctionnalités
62
+
63
+ - ✅ Manipulation de chaînes de caractères
64
+ - ✅ Opérations mathématiques
65
+ - ✅ Utilitaires de données
66
+ - ✅ Documentation complète
67
+ - ✅ Tests unitaires avec >80% de couverture
68
+
69
+ ## 📚 Documentation
70
+
71
+ La documentation complète est disponible sur [GitHub Pages](https://username.github.io/mon-package-python/).
72
+
73
+ ## 🧪 Tests
74
+
75
+ Pour exécuter les tests :
76
+
77
+ ```bash
78
+ poetry run pytest
79
+ ```
80
+
81
+ Avec couverture de code :
82
+
83
+ ```bash
84
+ poetry run pytest --cov
85
+ ```
86
+
87
+ ## 🔧 Développement
88
+
89
+ ### Pré-requis
90
+
91
+ - Python 3.10+
92
+ - Poetry
93
+
94
+ ### Installation de l'environnement de développement
95
+
96
+ ```bash
97
+ # Installer les dépendances
98
+ poetry install
99
+
100
+ # Activer l'environnement virtuel
101
+ poetry shell
102
+
103
+ # Installer les pre-commit hooks
104
+ pre-commit install
105
+ ```
106
+
107
+ ### Workflow de développement
108
+
109
+ 1. Créer une branche pour votre fonctionnalité
110
+ ```bash
111
+ git checkout -b feature/ma-nouvelle-fonctionnalite
112
+ ```
113
+
114
+ 2. Faire vos modifications avec des commits atomiques
115
+
116
+ 3. Exécuter les tests
117
+ ```bash
118
+ poetry run pytest
119
+ ```
120
+
121
+ 4. Vérifier la qualité du code
122
+ ```bash
123
+ ruff check --fix .
124
+ ```
125
+
126
+ 5. Créer une Pull Request
127
+
128
+ ## 📦 Structure du Projet
129
+
130
+ ```
131
+ mon-package-python/
132
+ ├── .github/
133
+ │ └── workflows/ # GitHub Actions CI/CD
134
+ ├── docs/ # Documentation Sphinx
135
+ ├── src/
136
+ │ └── mon_package_python/ # Code source
137
+ │ ├── __init__.py
138
+ │ ├── string_ops.py
139
+ │ └── math_ops.py
140
+ ├── tests/ # Tests unitaires
141
+ │ ├── __init__.py
142
+ │ ├── test_string_ops.py
143
+ │ └── test_math_ops.py
144
+ ├── .gitignore
145
+ ├── .pre-commit-config.yaml
146
+ ├── pyproject.toml
147
+ ├── README.md
148
+ └── LICENSE
149
+ ```
150
+
151
+
152
+
153
+
@@ -0,0 +1,129 @@
1
+ # Mon Package Python
2
+
3
+ [![Tests](https://github.com/username/mon-package-python/actions/workflows/tests.yaml/badge.svg)](https://github.com/username/mon-package-python/actions/workflows/tests.yaml)
4
+ [![Documentation](https://github.com/username/mon-package-python/actions/workflows/sphinx_doc.yaml/badge.svg)](https://username.github.io/mon-package-python/)
5
+ [![PyPI version](https://badge.fury.io/py/mon-package-python.svg)](https://badge.fury.io/py/mon-package-python)
6
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
7
+
8
+ Un package Python professionnel avec toutes les meilleures pratiques de développement.
9
+
10
+ ## 🚀 Installation
11
+
12
+ ```bash
13
+ pip install mon-package-python
14
+ ```
15
+
16
+ Ou pour un environnement de développement :
17
+
18
+ ```bash
19
+ git clone https://github.com/username/mon-package-python.git
20
+ cd mon-package-python
21
+ poetry install
22
+ ```
23
+
24
+ ## 📖 Utilisation
25
+
26
+ ```python
27
+ from mon_package_python.string_ops import reverse_string, count_vowels
28
+
29
+ # Inverser une chaîne
30
+ result = reverse_string("Hello World")
31
+ print(result) # "dlroW olleH"
32
+
33
+ # Compter les voyelles
34
+ vowels = count_vowels("Hello World")
35
+ print(vowels) # 3
36
+ ```
37
+
38
+ ## 🛠️ Fonctionnalités
39
+
40
+ - ✅ Manipulation de chaînes de caractères
41
+ - ✅ Opérations mathématiques
42
+ - ✅ Utilitaires de données
43
+ - ✅ Documentation complète
44
+ - ✅ Tests unitaires avec >80% de couverture
45
+
46
+ ## 📚 Documentation
47
+
48
+ La documentation complète est disponible sur [GitHub Pages](https://username.github.io/mon-package-python/).
49
+
50
+ ## 🧪 Tests
51
+
52
+ Pour exécuter les tests :
53
+
54
+ ```bash
55
+ poetry run pytest
56
+ ```
57
+
58
+ Avec couverture de code :
59
+
60
+ ```bash
61
+ poetry run pytest --cov
62
+ ```
63
+
64
+ ## 🔧 Développement
65
+
66
+ ### Pré-requis
67
+
68
+ - Python 3.10+
69
+ - Poetry
70
+
71
+ ### Installation de l'environnement de développement
72
+
73
+ ```bash
74
+ # Installer les dépendances
75
+ poetry install
76
+
77
+ # Activer l'environnement virtuel
78
+ poetry shell
79
+
80
+ # Installer les pre-commit hooks
81
+ pre-commit install
82
+ ```
83
+
84
+ ### Workflow de développement
85
+
86
+ 1. Créer une branche pour votre fonctionnalité
87
+ ```bash
88
+ git checkout -b feature/ma-nouvelle-fonctionnalite
89
+ ```
90
+
91
+ 2. Faire vos modifications avec des commits atomiques
92
+
93
+ 3. Exécuter les tests
94
+ ```bash
95
+ poetry run pytest
96
+ ```
97
+
98
+ 4. Vérifier la qualité du code
99
+ ```bash
100
+ ruff check --fix .
101
+ ```
102
+
103
+ 5. Créer une Pull Request
104
+
105
+ ## 📦 Structure du Projet
106
+
107
+ ```
108
+ mon-package-python/
109
+ ├── .github/
110
+ │ └── workflows/ # GitHub Actions CI/CD
111
+ ├── docs/ # Documentation Sphinx
112
+ ├── src/
113
+ │ └── mon_package_python/ # Code source
114
+ │ ├── __init__.py
115
+ │ ├── string_ops.py
116
+ │ └── math_ops.py
117
+ ├── tests/ # Tests unitaires
118
+ │ ├── __init__.py
119
+ │ ├── test_string_ops.py
120
+ │ └── test_math_ops.py
121
+ ├── .gitignore
122
+ ├── .pre-commit-config.yaml
123
+ ├── pyproject.toml
124
+ ├── README.md
125
+ └── LICENSE
126
+ ```
127
+
128
+
129
+
@@ -0,0 +1,79 @@
1
+ [tool.poetry]
2
+ name = "mon-package-python"
3
+ version = "0.1.0"
4
+ description = "Un package Python professionnel créé avec Poetry"
5
+ authors = ["Votre Nom <votre.email@example.com>"]
6
+ readme = "README.md"
7
+ license = "MIT"
8
+ homepage = "https://github.com/username/mon-package-python"
9
+ repository = "https://github.com/username/mon-package-python"
10
+ keywords = ["python", "package", "example"]
11
+ packages = [{include = "mon_package_python", from = "src"}]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ ]
22
+
23
+ [tool.poetry.dependencies]
24
+ python = "^3.10"
25
+
26
+ [tool.poetry.group.dev.dependencies]
27
+ pytest = "^9.0.0"
28
+ ruff = "^0.15.0"
29
+ # pytest-cov et sphinx sont désactivés pour Python 3.13 Microsoft Store
30
+
31
+ # pytest-cov = "^7.0.0"
32
+ # sphinx = "^7.2.0"
33
+ # sphinx-rtd-theme = "^1.3.0"
34
+ # pre-commit = "^3.5.0"
35
+
36
+ [build-system]
37
+ requires = ["poetry-core"]
38
+ build-backend = "poetry.core.masonry.api"
39
+
40
+ [tool.ruff]
41
+ line-length = 88
42
+ target-version = "py310"
43
+
44
+ [tool.ruff.lint]
45
+ select = [
46
+ "E", # pycodestyle errors
47
+ "W", # pycodestyle warnings
48
+ "F", # pyflakes
49
+ "I", # isort
50
+ "B", # flake8-bugbear
51
+ "C4", # flake8-comprehensions
52
+ "UP", # pyupgrade
53
+ ]
54
+ ignore = [
55
+ "E501", # line too long, handled by formatter
56
+ ]
57
+
58
+ [tool.ruff.format]
59
+ quote-style = "double"
60
+ indent-style = "space"
61
+
62
+ [tool.pytest.ini_options]
63
+ testpaths = ["tests"]
64
+ python_files = "test_*.py"
65
+ python_classes = "Test*"
66
+ python_functions = "test_*"
67
+ addopts = [
68
+ "--verbose",
69
+ "--strict-markers",
70
+ ]
71
+ # Options de coverage désactivées (pytest-cov non installé)
72
+ # Décommentez si vous installez pytest-cov:
73
+ # addopts = [
74
+ # "--verbose",
75
+ # "--strict-markers",
76
+ # "--cov=src",
77
+ # "--cov-report=term-missing",
78
+ # "--cov-report=html",
79
+ # ]
@@ -0,0 +1,26 @@
1
+ """
2
+ Mon Package Python
3
+ ==================
4
+
5
+ Un package Python professionnel avec toutes les meilleures pratiques.
6
+
7
+ Modules disponibles:
8
+ - string_ops: Opérations sur les chaînes de caractères
9
+ - math_ops: Opérations mathématiques utiles
10
+ """
11
+
12
+ __version__ = "0.1.0"
13
+ __author__ = "Votre Nom"
14
+ __email__ = "votre.email@example.com"
15
+
16
+ from mon_package_python.string_ops import (
17
+ capitalize_words,
18
+ count_vowels,
19
+ reverse_string,
20
+ )
21
+
22
+ __all__ = [
23
+ "reverse_string",
24
+ "count_vowels",
25
+ "capitalize_words",
26
+ ]
@@ -0,0 +1,210 @@
1
+ """
2
+ Module pour les opérations mathématiques.
3
+
4
+ Ce module fournit des fonctions mathématiques utilitaires.
5
+ """
6
+
7
+ from typing import List, Union
8
+
9
+
10
+ def factorial(n: int) -> int:
11
+ """
12
+ Calcule la factorielle d'un nombre.
13
+
14
+ Args:
15
+ n (int): Le nombre dont on veut calculer la factorielle (n >= 0).
16
+
17
+ Returns:
18
+ int: La factorielle de n.
19
+
20
+ Examples:
21
+ >>> factorial(5)
22
+ 120
23
+ >>> factorial(0)
24
+ 1
25
+ >>> factorial(3)
26
+ 6
27
+
28
+ Raises:
29
+ ValueError: Si n est négatif.
30
+ TypeError: Si n n'est pas un entier.
31
+ """
32
+ if not isinstance(n, int):
33
+ raise TypeError(f"Expected int, got {type(n).__name__}")
34
+ if n < 0:
35
+ raise ValueError("Factorial is not defined for negative numbers")
36
+ if n == 0 or n == 1:
37
+ return 1
38
+ return n * factorial(n - 1)
39
+
40
+
41
+ def is_prime(n: int) -> bool:
42
+ """
43
+ Vérifie si un nombre est premier.
44
+
45
+ Args:
46
+ n (int): Le nombre à vérifier.
47
+
48
+ Returns:
49
+ bool: True si le nombre est premier, False sinon.
50
+
51
+ Examples:
52
+ >>> is_prime(7)
53
+ True
54
+ >>> is_prime(10)
55
+ False
56
+ >>> is_prime(2)
57
+ True
58
+
59
+ Raises:
60
+ TypeError: Si n n'est pas un entier.
61
+ """
62
+ if not isinstance(n, int):
63
+ raise TypeError(f"Expected int, got {type(n).__name__}")
64
+ if n < 2:
65
+ return False
66
+ if n == 2:
67
+ return True
68
+ if n % 2 == 0:
69
+ return False
70
+
71
+ # Vérifier les diviseurs impairs jusqu'à sqrt(n)
72
+ i = 3
73
+ while i * i <= n:
74
+ if n % i == 0:
75
+ return False
76
+ i += 2
77
+ return True
78
+
79
+
80
+ def fibonacci(n: int) -> List[int]:
81
+ """
82
+ Génère la suite de Fibonacci jusqu'au n-ième terme.
83
+
84
+ Args:
85
+ n (int): Le nombre de termes à générer (n >= 0).
86
+
87
+ Returns:
88
+ List[int]: La liste des n premiers termes de Fibonacci.
89
+
90
+ Examples:
91
+ >>> fibonacci(5)
92
+ [0, 1, 1, 2, 3]
93
+ >>> fibonacci(8)
94
+ [0, 1, 1, 2, 3, 5, 8, 13]
95
+ >>> fibonacci(1)
96
+ [0]
97
+
98
+ Raises:
99
+ ValueError: Si n est négatif.
100
+ TypeError: Si n n'est pas un entier.
101
+ """
102
+ if not isinstance(n, int):
103
+ raise TypeError(f"Expected int, got {type(n).__name__}")
104
+ if n < 0:
105
+ raise ValueError("n must be non-negative")
106
+ if n == 0:
107
+ return []
108
+ if n == 1:
109
+ return [0]
110
+
111
+ fib = [0, 1]
112
+ for _ in range(2, n):
113
+ fib.append(fib[-1] + fib[-2])
114
+ return fib
115
+
116
+
117
+ def mean(numbers: List[Union[int, float]]) -> float:
118
+ """
119
+ Calcule la moyenne d'une liste de nombres.
120
+
121
+ Args:
122
+ numbers (List[Union[int, float]]): Liste de nombres.
123
+
124
+ Returns:
125
+ float: La moyenne des nombres.
126
+
127
+ Examples:
128
+ >>> mean([1, 2, 3, 4, 5])
129
+ 3.0
130
+ >>> mean([10, 20, 30])
131
+ 20.0
132
+
133
+ Raises:
134
+ ValueError: Si la liste est vide.
135
+ TypeError: Si la liste contient des éléments non numériques.
136
+ """
137
+ if not numbers:
138
+ raise ValueError("Cannot calculate mean of empty list")
139
+ if not all(isinstance(x, (int, float)) for x in numbers):
140
+ raise TypeError("All elements must be numbers")
141
+
142
+ return sum(numbers) / len(numbers)
143
+
144
+
145
+ def median(numbers: List[Union[int, float]]) -> float:
146
+ """
147
+ Calcule la médiane d'une liste de nombres.
148
+
149
+ Args:
150
+ numbers (List[Union[int, float]]): Liste de nombres.
151
+
152
+ Returns:
153
+ float: La médiane des nombres.
154
+
155
+ Examples:
156
+ >>> median([1, 2, 3, 4, 5])
157
+ 3.0
158
+ >>> median([1, 2, 3, 4])
159
+ 2.5
160
+
161
+ Raises:
162
+ ValueError: Si la liste est vide.
163
+ TypeError: Si la liste contient des éléments non numériques.
164
+ """
165
+ if not numbers:
166
+ raise ValueError("Cannot calculate median of empty list")
167
+ if not all(isinstance(x, (int, float)) for x in numbers):
168
+ raise TypeError("All elements must be numbers")
169
+
170
+ sorted_numbers = sorted(numbers)
171
+ n = len(sorted_numbers)
172
+
173
+ if n % 2 == 1:
174
+ return float(sorted_numbers[n // 2])
175
+ else:
176
+ mid1, mid2 = sorted_numbers[n // 2 - 1], sorted_numbers[n // 2]
177
+ return (mid1 + mid2) / 2.0
178
+
179
+
180
+ def gcd(a: int, b: int) -> int:
181
+ """
182
+ Calcule le plus grand commun diviseur (PGCD) de deux nombres.
183
+
184
+ Utilise l'algorithme d'Euclide.
185
+
186
+ Args:
187
+ a (int): Premier nombre.
188
+ b (int): Deuxième nombre.
189
+
190
+ Returns:
191
+ int: Le PGCD de a et b.
192
+
193
+ Examples:
194
+ >>> gcd(48, 18)
195
+ 6
196
+ >>> gcd(100, 50)
197
+ 50
198
+ >>> gcd(17, 13)
199
+ 1
200
+
201
+ Raises:
202
+ TypeError: Si a ou b ne sont pas des entiers.
203
+ """
204
+ if not isinstance(a, int) or not isinstance(b, int):
205
+ raise TypeError("Both arguments must be integers")
206
+
207
+ a, b = abs(a), abs(b)
208
+ while b:
209
+ a, b = b, a % b
210
+ return a
@@ -0,0 +1,144 @@
1
+ """
2
+ Module pour les opérations sur les chaînes de caractères.
3
+
4
+ Ce module fournit des fonctions utilitaires pour manipuler
5
+ les chaînes de caractères de manière efficace.
6
+ """
7
+
8
+
9
+ def reverse_string(s: str) -> str:
10
+ """
11
+ Inverse l'ordre des caractères d'une chaîne.
12
+
13
+ Args:
14
+ s (str): La chaîne à inverser.
15
+
16
+ Returns:
17
+ str: La chaîne inversée.
18
+
19
+ Examples:
20
+ >>> reverse_string("Hello")
21
+ 'olleH'
22
+ >>> reverse_string("Python")
23
+ 'nohtyP'
24
+
25
+ Raises:
26
+ TypeError: Si l'argument n'est pas une chaîne.
27
+ """
28
+ if not isinstance(s, str):
29
+ raise TypeError(f"Expected str, got {type(s).__name__}")
30
+ return s[::-1]
31
+
32
+
33
+ def count_vowels(s: str) -> int:
34
+ """
35
+ Compte le nombre de voyelles dans une chaîne.
36
+
37
+ Les voyelles prises en compte sont: a, e, i, o, u (minuscules et majuscules).
38
+
39
+ Args:
40
+ s (str): La chaîne dans laquelle compter les voyelles.
41
+
42
+ Returns:
43
+ int: Le nombre de voyelles trouvées.
44
+
45
+ Examples:
46
+ >>> count_vowels("Hello World")
47
+ 3
48
+ >>> count_vowels("AEIOU")
49
+ 5
50
+ >>> count_vowels("xyz")
51
+ 0
52
+
53
+ Raises:
54
+ TypeError: Si l'argument n'est pas une chaîne.
55
+ """
56
+ if not isinstance(s, str):
57
+ raise TypeError(f"Expected str, got {type(s).__name__}")
58
+
59
+ vowels: str = "aeiouAEIOU"
60
+ return sum(1 for char in s if char in vowels)
61
+
62
+
63
+ def capitalize_words(s: str) -> str:
64
+ """
65
+ Met en majuscule la première lettre de chaque mot.
66
+
67
+ Args:
68
+ s (str): La chaîne à modifier.
69
+
70
+ Returns:
71
+ str: Une nouvelle chaîne avec chaque mot capitalisé.
72
+
73
+ Examples:
74
+ >>> capitalize_words("hello world")
75
+ 'Hello World'
76
+ >>> capitalize_words("python programming")
77
+ 'Python Programming'
78
+ >>> capitalize_words("the quick brown fox")
79
+ 'The Quick Brown Fox'
80
+
81
+ Raises:
82
+ TypeError: Si l'argument n'est pas une chaîne.
83
+ """
84
+ if not isinstance(s, str):
85
+ raise TypeError(f"Expected str, got {type(s).__name__}")
86
+
87
+ return " ".join(word.capitalize() for word in s.split())
88
+
89
+
90
+ def remove_whitespace(s: str) -> str:
91
+ """
92
+ Supprime tous les espaces blancs d'une chaîne.
93
+
94
+ Args:
95
+ s (str): La chaîne à traiter.
96
+
97
+ Returns:
98
+ str: La chaîne sans espaces.
99
+
100
+ Examples:
101
+ >>> remove_whitespace("Hello World")
102
+ 'HelloWorld'
103
+ >>> remove_whitespace(" Python Programming ")
104
+ 'PythonProgramming'
105
+
106
+ Raises:
107
+ TypeError: Si l'argument n'est pas une chaîne.
108
+ """
109
+ if not isinstance(s, str):
110
+ raise TypeError(f"Expected str, got {type(s).__name__}")
111
+
112
+ return "".join(s.split())
113
+
114
+
115
+ def is_palindrome(s: str) -> bool:
116
+ """
117
+ Vérifie si une chaîne est un palindrome.
118
+
119
+ Un palindrome est un mot qui se lit de la même façon dans les deux sens.
120
+ La fonction ignore les espaces et la casse.
121
+
122
+ Args:
123
+ s (str): La chaîne à vérifier.
124
+
125
+ Returns:
126
+ bool: True si c'est un palindrome, False sinon.
127
+
128
+ Examples:
129
+ >>> is_palindrome("radar")
130
+ True
131
+ >>> is_palindrome("A man a plan a canal Panama")
132
+ True
133
+ >>> is_palindrome("hello")
134
+ False
135
+
136
+ Raises:
137
+ TypeError: Si l'argument n'est pas une chaîne.
138
+ """
139
+ if not isinstance(s, str):
140
+ raise TypeError(f"Expected str, got {type(s).__name__}")
141
+
142
+ # Normaliser: enlever les espaces et mettre en minuscules
143
+ normalized = "".join(s.split()).lower()
144
+ return normalized == normalized[::-1]