@@ -4,391 +4,9 @@ pyvalid
4
4
.. image :: https://img.shields.io/codecov/c/github/uzumaxy/pyvalid.svg?style=plastic
5
5
.. image :: https://img.shields.io/github/workflow/status/uzumaxy/pyvalid/Python%20package?style=plastic
6
6
7
- The pyvalid is the Python validation tool for checking a function's input
8
- parameters and return values.
9
-
10
- Purposes of the pyvalid package:
11
-
12
- #. Provide an ability to validate a user input (such as usernames,
13
- phone numbers, emails, dates and times, etc) and minimize the amount of
14
- code required for the implementation of the comprehensive validation
15
- systems;
16
- #. Add an additional layer of dynamic code analysis for the development and
17
- testing stages — pyvalid will raise the exception if a function accepts or
18
- returns unexpected values and it's always possible to disable pyvalid in
19
- production if needed.
20
- #. Help to catch runtime issues.
21
-
22
-
23
- How to install
24
- ++++++++++++++
25
-
26
- * With PyPI: ``pip install -U pyvalid ``
27
- * Manually: ``python setup.py install ``
28
-
29
-
30
- How to use
31
- ++++++++++
32
-
33
- The schema below reveals the general structure of the ``pyvalid `` package:
34
-
35
- .. image :: https://raw.githubusercontent.com/uzumaxy/pyvalid/master/docs/assets/pyvalid-map.png
36
- :width: 600
37
-
38
- The package consists of two decorators: ``accepts `` and ``returns ``, which
39
- validates the function’s input and output values accordingly. To know how to
40
- validate the data, ``accepts `` and ``returns `` decorators should receive the
41
- information about excepted values/types/validators.
42
-
43
- The very basic example below shows how to use ``accepts `` and ``returns ``
44
- decorators.
45
-
46
- .. code-block :: python
47
-
48
- from pyvalid import accepts, returns
49
-
50
-
51
- @accepts (int , int )
52
- @returns (float )
53
- def divide (num_1 , num_2 ):
54
- return num_1 / num_2
55
-
56
- divide(8 , 42 )
57
- # Returns float value
58
-
59
- divide(' Python' , 42 )
60
- # Raises the ArgumentValidationError exception, since the 1st argument is
61
- # the str value, when we're expecting int values only.
62
-
63
- If just specifying an expected type or value is not enough, then it's worth to
64
- use the custom validator. All the built-in validators are located in the
65
- ``pyvalid.validators `` module and it's also possible to create a new one using
66
- the ``is_validator `` decorator or through extending the ``AbstractValidator ``
67
- class.
68
-
69
- We can flexibly control the state of the ``pyvalid `` validation using the
70
- ``pyvalid.switch `` module. This module provides an ability to switch the
71
- ``pyvalid `` on/off.
72
-
73
- In most cases, it's worth to use the ``pyvalid `` features to validate
74
- incoming/outcoming data, such as: user input, the data sent to the API, etc.
75
-
76
- But it's also possible to use the ``pyvalid `` package as a part of the CI/CD
77
- processes only:
78
-
79
- #. Apply the ``accepts `` and ``returns `` decorators to all needed functions
80
- and methods.
81
- #. Perform unit testing, integration testing, etc.
82
- #. The ``accepts `` and ``returns `` decorators will raise exceptions in case if
83
- the input/output data is not valid.
84
- #. Collect information about raised exceptions and fix the code, which causes
85
- them.
86
- #. Turn off the ``pyvalid `` before going live in order to avoid unnecessary
87
- exceptions in production.
88
-
89
-
90
- ``pyvalid.accepts(*allowed_arg_values, **allowed_kwargs_values) ``
91
- -----------------------------------------------------------------
92
-
93
- The decorator which validates input parameters of the wrapped function.
94
-
95
- To use it, we need to specify the list of allowed types or values. If the
96
- function’s input doesn’t match the allowed types/values, one of the following
97
- errors will be thrown:
98
-
99
- * ``pyvalid.ArgumentValidationError `` — when the actual type/value of the
100
- function’s argument is different from the expected one;
101
- * ``pyvalid.InvalidArgumentNumberError `` — when the number/position of
102
- function’s arguments is incorrect.
103
-
104
- Examples of usage:
105
-
106
- Let's define the ``multiply ``, which accepts only ``int `` values, and see how
107
- does it work with other types.
108
-
109
- .. code-block :: python
110
-
111
- from pyvalid import accepts
112
-
113
-
114
- @accepts (int , int )
115
- def multiply (num_1 , num_2 ):
116
- return num_1 * num_2
117
-
118
-
119
- multiply(4 , 2 )
120
- # Returns 8.
121
-
122
- multiply(3.14 , 8 )
123
- # Raises the ArgumentValidationError exception, since the 1st argument is
124
- # the float value, when we're expecting int values only.
125
-
126
- multiply(3 , ' pyvalid' )
127
- # Raises the ArgumentValidationError exception, since the 2nd argument is
128
- # the str value, when we're expecting int values only.
129
-
130
- multiply(128 )
131
- # Raises the InvalidArgumentNumberError exception, since the second
132
- # argument is missing.
133
-
134
-
135
- ``pyvalid.returns(*allowed_return_values) ``
136
- -------------------------------------------
137
-
138
- The decorator which validates the value returned by the wrapped function.
139
-
140
- To use it, we need to specify the list of expected return types or values.
141
- If the function’s return value doesn’t match the allowed types/values, the
142
- ``pyvalid.InvalidReturnTypeError `` error will be thrown.
143
-
144
- Examples of usage:
145
-
146
- Let's define the ``multiply ``, which returns only ``int `` values, and see how
147
- does it work with other types.
148
-
149
- .. code-block :: python
150
-
151
- from pyvalid import returns
152
-
153
-
154
- @returns (int )
155
- def multiply (num_1 , num_2 ):
156
- return num_1 * num_2
157
-
158
-
159
- multiply(4 , 2 )
160
- # Returns 8.
161
-
162
- multiply(3.14 , 8 )
163
- # Raises the InvalidReturnTypeError exception, since the function returns
164
- # the float value, when we're expecting int values only.
165
-
166
- multiply(3 , ' pyvalid' )
167
- # Raises the InvalidReturnTypeError exception, since the function returns
168
- # the str value, when we're expecting int values only.
169
-
170
-
171
- Advanced examples
172
- +++++++++++++++++
173
-
174
- Function ``calculate `` in the example below has the following limitations:
175
-
176
- * Function should return ``int `` or ``float `` values only;
177
- * First parameter must be ``str `` value;
178
- * Second parameter must be ``int `` value or be equal to the ``2.0 ``;
179
- * Third parameter must be ``int `` or ``float `` value.
180
-
181
- .. code-block :: python
182
-
183
- from pyvalid import accepts, returns
184
-
185
-
186
- @returns (int , float )
187
- @accepts (str , (int , 2.0 ), (int , float ))
188
- def calculate (operator , val1 , val2 , val3 ):
189
- expression = ' {v1} {op} {v2} {op} {v3} ' .format(
190
- op = operator,
191
- v1 = val1, v2 = val2, v3 = val3
192
- )
193
- return eval (expression)
194
-
195
-
196
- calculate(' *' , 2 , 3 , 4 )
197
- # Returns 24.
198
-
199
- calculate(operator = ' *' , val1 = 2 , val2 = 3.0 , val3 = 4 )
200
- # Returns 24.0.
201
-
202
- calculate(' *' , 2.0 , 3 , 4 )
203
- # Still returns 24.0.
204
-
205
- calculate(' *' , 3.14 , 3 , 4 )
206
- # Raises the ArgumentValidationError exception, because the second
207
- # argument is not valid.
208
-
209
- calculate(' *' , 2 , 3 , ' "4"' )
210
- # Raises the InvalidReturnTypeError exception, because of invalid return
211
- # value: function returns the str value, when only int and float values
212
- # are allowed.
213
-
214
-
215
- The example below demonstrates how to use the ``accepts `` and ``returns ``
216
- decorators in the classes. Please pay attention to the method ``connect `` of
217
- the class ``SqlDriver ``. In these classes we're using the ``accepts ``
218
- decorator to validate keyword arguments.
219
-
220
- .. code-block :: python
221
-
222
- from pyvalid import accepts, returns
223
- from collections.abc import Iterable
224
-
225
-
226
- class SqlDriver (object ):
227
-
228
- @returns (bool )
229
- @accepts (object , host = str , port = int , usr = str , pwd = str , db = [str , None ])
230
- def connect (self , ** kwargs ):
231
- conn_req = ' tsql -S {host} -p {port} -U {usr} -P {pwd} -D {db} '
232
- conn_req = conn_req.format(** kwargs)
233
- try :
234
- print (' Establishing connection: "{} "' .format(conn_req))
235
- # Some code, which may cause the ConnectionError
236
- return True
237
- except ConnectionError :
238
- return False
239
-
240
- @returns (bool )
241
- def close (self ):
242
- try :
243
- print (' Closing connection' )
244
- # Some code, which may cause the ConnectionError
245
- return True
246
- except ConnectionError :
247
- return False
248
-
249
- @returns (None , dict )
250
- @accepts (object , str , Iterable)
251
- def query (self , sql , params = None ):
252
- try :
253
- if params is not None :
254
- sql = sql.format(* params)
255
- query_info = ' Processing request "{} "' .format(sql)
256
- print (query_info)
257
- return dict ()
258
- # Some code, which may cause the ConnectionError
259
- except ConnectionError :
260
- return None
261
-
262
-
263
- sql_driver = SqlDriver()
264
-
265
- conn_params = {
266
- ' host' : ' 8.8.8.8' ,
267
- ' port' : 1433 ,
268
- ' usr' : ' admin' ,
269
- ' pwd' : ' password' ,
270
- ' db' : ' wiki'
271
- }
272
- sql_driver.connect(** conn_params)
273
-
274
- sql = ' SELECT * FROM ProgrammingLang'
275
- pl = sql_driver.query(sql)
276
-
277
- sql = ' SELECT * FROM ProgrammingLang WHERE name={} '
278
- python_pl = sql_driver.query(sql, (' Python' ,))
279
-
280
- sql_driver.close()
281
-
282
-
283
- When we need a bit more complex validators, we may use built-in ``pyvalid`
284
- validators available in the ``pyvalid.validators `` module.
285
- For example, here we're using the ``StringValidator `` validator based on the
286
- regular expression and the ``NumberValidator `` based on the min/max allowed
287
- values:
288
-
289
- .. code-block :: python
290
-
291
- from pyvalid import accepts, returns
292
- from pyvalid.validators import NumberValidator, StringValidator
293
-
294
- @accepts (StringValidator(re_pattern = r ' ^ [A-Za-z ]+ \s ? [A-Za-z ]+ \s ? [A-Za-z ]+ $ ' ))
295
- @returns (NumberValidator(min_val = 0 , max_val = 10 ))
296
- def get_review (name ):
297
- message = ' Hello, {} ! Please review our application from 0 to 10.'
298
- print (message.format(name))
299
- return float (input ())
300
-
301
- review = get_review(' Elon Musk' )
302
- print (review)
303
- # Will raise the InvalidReturnTypeError exception only if user enter
304
- # the value, which is not in the [0, 10] range.
305
-
306
- another_review = get_review(' Elon Musk 2' )
307
- # Raises the ArgumentValidationError exception, since the "Elon Musk 2"
308
- # value doesn't match the pattern.
309
-
310
-
311
- The example below explains how to use the custom validator. It's pretty
312
- easy actually, we just need to apply the ``pyvalid.validators.is_validator ``
313
- decorator to the validation function.
314
-
315
- .. code-block :: python
316
-
317
- from pyvalid import accepts
318
- from pyvalid.validators import is_validator
319
-
320
-
321
- class User (object ):
322
-
323
- registered_users = list ()
324
-
325
- class Validator (object ):
326
-
327
- unsafe_passwords = [
328
- ' 111111' , ' 000000' , ' 123123' ,
329
- ' 123456' , ' 12345678' , ' 1234567890' ,
330
- ' qwerty' , ' sunshine' , ' password' ,
331
- ]
332
-
333
- @ classmethod
334
- @is_validator
335
- def login_checker (cls , login ):
336
- if isinstance (login, str ) and 1 <= len (login) <= 16 :
337
- for reg_user in User.registered_users:
338
- if login == reg_user.login:
339
- return False
340
- return True
341
-
342
- @ classmethod
343
- @is_validator
344
- def password_checker (cls , password ):
345
- return (
346
- isinstance (password, str )
347
- and
348
- 6 <= len (password) <= 32
349
- and
350
- password not in cls .unsafe_passwords
351
- )
352
-
353
- def __init__ (self , login , password ):
354
- self .__login = None
355
- self .login = login
356
- self .__password = None
357
- self .password = password
358
- User.registered_users.append(self )
359
-
360
- @ property
361
- def login (self ):
362
- return self .__login
363
-
364
- @login.setter
365
- @accepts (object , Validator.login_checker)
366
- def login (self , value ):
367
- self .__login = value
368
-
369
- @ property
370
- def password (self ):
371
- return self .__password
372
-
373
- @password.setter
374
- @accepts (object , Validator.password_checker)
375
- def password (self , value ):
376
- self .__password = value
377
-
378
-
379
- user = User(' admin' , ' Str0ng_P@ssw0rd!' )
380
-
381
- print (user.login, user.password)
382
- # Outputs: "admin Str0ng_P@ssw0rd!"
383
-
384
- user.password = ' qwerty'
385
- # Raises the ArgumentValidationError exception, because the
386
- # User.Validator.password_checker method returns False.
387
-
388
- user = User(' admin' , ' An0ther_Str0ng_P@ssw0rd!' )
389
- # Raises the ArgumentValidationError exception, because the
390
- # User.Validator.login_checker method returns False.
7
+ pyvalid is a Python data validation tool.
391
8
9
+ Documentation: `uzumaxy.github.io/pyvalid <https://uzumaxy.github.io/pyvalid/ >`_.
392
10
393
11
License
394
12
+++++++
0 commit comments