Commit c5fb1b6a308cefda77351b0808321836756c75cf
1 parent
50af407d
Exists in
dev
new my_package1.my_module1
Showing
1 changed file
with
188 additions
and
13 deletions
Show diff stats
doc/code_style/my_package1/my_module1.py
1 | 1 | #!/usr/bin/env python3 |
2 | 2 | |
3 | +# Sphinx Napoleon syntax : https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html | |
4 | + | |
5 | +# This import must be at the very beginning of the file | |
3 | 6 | from __future__ import annotations |
4 | 7 | |
5 | 8 | # TODO : |
... | ... | @@ -36,9 +39,10 @@ from __future__ import annotations |
36 | 39 | |
37 | 40 | # --- GENERAL PURPOSE IMPORTS --- |
38 | 41 | |
39 | -from typing import Dict, List, Tuple | |
42 | +from typing import Dict, List, Tuple, Sequence, Any, TypeVar, Union, Callable | |
40 | 43 | import platform |
41 | 44 | from datetime import date |
45 | +import random | |
42 | 46 | |
43 | 47 | # --- PROJECT SPECIFIC IMPORTS --- |
44 | 48 | |
... | ... | @@ -67,13 +71,16 @@ IS_WINDOWS = platform.system() == "Windows" |
67 | 71 | # - General Functions |
68 | 72 | # |
69 | 73 | |
70 | -def general_function_that_returns_a_float(arg_a: int, arg_b: str, arg_c: float=1.2, arg_d: bool=True) -> float: | |
71 | - ''' | |
72 | - This function is used for ... blabla ... | |
74 | +# | |
75 | +# Typehint : Union (but not yet '|', only available with python 3.10) | |
76 | +# | |
77 | + | |
78 | +def general_function_that_returns_a_float(arg_a: int, arg_b: Union[str, int, float], arg_c: float=1.2, arg_d: bool=True) -> float: | |
79 | + ''' This function is used for ... blabla ... | |
73 | 80 | |
74 | 81 | Args: |
75 | 82 | arg_a: the path of the file to wrap |
76 | - arg_b: instance to wrap | |
83 | + arg_b: instance to wrap (in Python 3.10 we will be able to use 'str | int | float' instead) | |
77 | 84 | arg_c: toto |
78 | 85 | arg_d: whether or not to delete the file when the File instance is destructed |
79 | 86 | |
... | ... | @@ -94,8 +101,11 @@ def general_function_that_returns_a_float(arg_a: int, arg_b: str, arg_c: float=1 |
94 | 101 | |
95 | 102 | return 3.5 |
96 | 103 | |
104 | +# | |
105 | +# Typehint : Tuple (immutable) | |
106 | +# | |
97 | 107 | |
98 | -def general_function_that_returns_a_tuple_of_3_elem(a: int, b: int=2, c: str='titi') -> Tuple[str, float, str]: | |
108 | +def general_function_that_returns_a_tuple_of_3_elem(a: int, b: int=2, c: str='titi') -> Tuple[int, float, str]: | |
99 | 109 | ''' Commentaire général sur la fonction |
100 | 110 | |
101 | 111 | Args: |
... | ... | @@ -107,19 +117,181 @@ def general_function_that_returns_a_tuple_of_3_elem(a: int, b: int=2, c: str='ti |
107 | 117 | return (a, b, c+' toto') |
108 | 118 | |
109 | 119 | |
110 | -class MySuperClass1: | |
111 | - pass | |
120 | +# | |
121 | +# Typehint : Sequence = List (mutable) or Tuple (immutable) | |
122 | +# | |
112 | 123 | |
124 | +def square(elems: Sequence[float]) -> List[float]: | |
125 | + ''' function that takes a Sequence (either List of Tuple) as input, and send a List as output | |
113 | 126 | |
114 | -class MySuperClass2: | |
127 | + Usage: | |
128 | + | |
129 | + >>> square( [1,2,3] ) | |
130 | + [1, 4, 9] | |
131 | + >>> square( (1,2,3) ) | |
132 | + [1, 4, 9] | |
133 | + ''' | |
134 | + | |
135 | + return [x**2 for x in elems] | |
136 | + | |
137 | + | |
138 | +# | |
139 | +# Typehint : TypeAlias | |
140 | +# | |
141 | + | |
142 | +SUITS = "♠ ♡ ♢ ♣".split() | |
143 | +RANKS = "2 3 4 5 6 7 8 9 10 J Q K A".split() | |
144 | + | |
145 | +# Type aliases | |
146 | +Card = Tuple[str, str] | |
147 | +Deck = List[Card] | |
148 | + | |
149 | + | |
150 | +def create_deck_without_alias(shuffle: bool = False) -> List[Tuple[str, str]]: | |
151 | + """Create a new deck of 52 cards""" | |
152 | + deck = [(s, r) for r in RANKS for s in SUITS] | |
153 | + if shuffle: | |
154 | + random.shuffle(deck) | |
155 | + return deck | |
156 | + | |
157 | + | |
158 | +def create_deck_with_alias(shuffle: bool = False) -> Deck: | |
159 | + """Create a new deck of 52 cards | |
160 | + | |
161 | + Card = Tuple[str, str] | |
162 | + | |
163 | + Deck = List[Card] | |
164 | + | |
165 | + Return Deck | |
166 | + """ | |
167 | + deck = [(s, r) for r in RANKS for s in SUITS] | |
168 | + if shuffle: | |
169 | + random.shuffle(deck) | |
170 | + return deck | |
171 | + | |
172 | + | |
173 | +# | |
174 | +# Typehint : Generics avec Sequence, Any, TypeVar | |
175 | +# | |
176 | + | |
177 | +def choose_from_list_of_Any_returns_a_Any(items: Sequence[Any]) -> Any: | |
178 | + ''' Option1 (BAD) : avoid using 'Any' because too general ''' | |
179 | + return random.choice(items) | |
180 | + | |
181 | + | |
182 | +T = TypeVar('T') | |
183 | +def choose_from_list_of_a_specific_type_returns_same_type(items: Sequence[T]) -> T: | |
184 | + ''' Option2 (BETTER) : prefer 'TypeVar' instead of 'Any' | |
185 | + | |
186 | + T = TypeVar("T") | |
187 | + ''' | |
188 | + return random.choice(items) | |
189 | + | |
190 | + | |
191 | +T = TypeVar("T", str, float) | |
192 | +def choose_from_list_of_a_specific_constrained_type_returns_same_type(items: Sequence[T]) -> T: | |
193 | + ''' Option3 (still BETTER) : use a 'constrained TypeVar' | |
194 | + | |
195 | + T = TypeVar("T", str, float) | |
196 | + | |
197 | + (you could name 'T' as you want, for example, 'Choosable'...) | |
198 | + | |
199 | + => the function accepts only sequence of str or float: | |
200 | + | |
201 | + - if str: return str | |
202 | + | |
203 | + - if float: return float | |
204 | + | |
205 | + Usage: | |
206 | + | |
207 | + choose(["Guido", "Jukka", "Ivan"]) => str, OK | |
208 | + | |
209 | + choose([1, 2, 3]) => float, OK (car int subtype of float) | |
210 | + | |
211 | + choose([True, 42, 3.14]) => float, OK (car bool subtype of int which is subtype of float) | |
212 | + | |
213 | + choose(["Python", 3, 7]) => object, KO (rejected) | |
214 | + ''' | |
215 | + | |
216 | + return random.choice(items) | |
217 | + | |
218 | + | |
219 | +# | |
220 | +# Typehint : Callable | |
221 | +# | |
222 | + | |
223 | +def create_greeting(congrat: str, name: str, nb: int) -> str: | |
224 | + #return f"Hello {name}" | |
225 | + return f"{congrat} {name} {nb}" | |
226 | + | |
227 | + | |
228 | +def do_twice(func: Callable[[str, str, int], str], arg1: str, arg2: str, arg3: int) -> None: | |
229 | + ''' | |
230 | + Usage: | |
231 | + | |
232 | + >>> do_twice(create_greeting, "Hello", "Jekyll", 1) | |
233 | + Hello Jekyll 1 | |
234 | + Hello Jekyll 1 | |
235 | + ''' | |
236 | + print(func(arg1, arg2, arg3)) | |
237 | + print(func(arg1, arg2, arg3)) | |
238 | + | |
239 | + | |
240 | +# ''' | |
241 | +# ******************************* | |
242 | +# CUSTOM EXCEPTIONS | |
243 | +# ******************************* | |
244 | +# See https://docs.python.org/3/tutorial/errors.html | |
245 | +# ''' | |
246 | + | |
247 | + | |
248 | +class DCCNotFoundException(Exception): | |
249 | + ''' Raised when a specific DCC is not available ''' | |
115 | 250 | pass |
116 | 251 | |
117 | 252 | |
253 | +class UnknownGenericCmdArgException(Exception): | |
254 | + ''' Raised when a GENERIC cmd argument is not recognized by the controller (no native cmd available for the generic cmd) ''' | |
255 | + | |
256 | + def __init__(self, name: str, arg: str): | |
257 | + self.name = name | |
258 | + self.arg = arg | |
259 | + | |
260 | + def __str__(self): | |
261 | + return f"The argument '{self.arg}' does not exist for generic cmd {self.name}" | |
262 | + | |
263 | + | |
264 | +class UnknownNativeCmdException(Exception): | |
265 | + ''' Raised when a NATIVE command name is not recognized by the controller ''' | |
266 | + def __init__(self, *args, **kwargs): | |
267 | + super().__init__(self, *args, **kwargs) | |
268 | + | |
269 | + | |
270 | +class UnimplementedGenericCmdException(Exception): | |
271 | + ''' Raised when a GENERIC cmd has no implementation in the controller (no native cmd available for the generic cmd) ''' | |
272 | + def __str__(self): | |
273 | + return f"({type(self).__name__}): Device Generic command has no implementation in the controller" | |
274 | + | |
275 | + | |
118 | 276 | # ''' |
119 | 277 | # ================================================================= |
120 | -# CLASS MyFirstClass | |
278 | +# CLASSES | |
121 | 279 | # ================================================================= |
122 | 280 | # ''' |
281 | + | |
282 | + | |
283 | +class MySuperClass1: | |
284 | + pass | |
285 | + | |
286 | + | |
287 | +class MySuperClass2: | |
288 | + pass | |
289 | + | |
290 | + | |
291 | +# | |
292 | +# CLASS MyFirstClass | |
293 | +# | |
294 | + | |
123 | 295 | class MySimpleClass(MySuperClass1, MySuperClass2): |
124 | 296 | ''' a Class with multi-inheritance |
125 | 297 | |
... | ... | @@ -129,7 +301,7 @@ class MySimpleClass(MySuperClass1, MySuperClass2): |
129 | 301 | ''' |
130 | 302 | |
131 | 303 | # |
132 | - # The class attributes | |
304 | + # Class attributes | |
133 | 305 | # |
134 | 306 | |
135 | 307 | names: List[str] = ["Guido", "Jukka", "Ivan"] |
... | ... | @@ -146,7 +318,7 @@ class MySimpleClass(MySuperClass1, MySuperClass2): |
146 | 318 | pickle_file = "obsconfig.p" |
147 | 319 | |
148 | 320 | # |
149 | - # The class methods | |
321 | + # Class methods | |
150 | 322 | # |
151 | 323 | |
152 | 324 | def __init__(self, a: int, b: float) -> None: |
... | ... | @@ -218,6 +390,9 @@ class Person: |
218 | 390 | # a class method to create a Person object by birth year |
219 | 391 | @classmethod |
220 | 392 | def fromBirthYear(cls, name: str, year: int) -> Person: |
393 | + ''' | |
394 | + NB : return type 'Person' is possible because of: 'from __future__ import annotations' | |
395 | + ''' | |
221 | 396 | return cls(name, date.today().year - year) |
222 | 397 | |
223 | 398 | # a class method to create a Person object from another |
... | ... | @@ -247,7 +422,7 @@ def main() -> None: |
247 | 422 | c = a+b |
248 | 423 | |
249 | 424 | a = general_function_that_returns_a_tuple_of_3_elem(1, 2) |
250 | - print(a) | |
425 | + #print(a) | |
251 | 426 | |
252 | 427 | import doctest |
253 | 428 | doctest.testmod() | ... | ... |