Commit c5fb1b6a308cefda77351b0808321836756c75cf

Authored by Etienne Pallier
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()
... ...