Skip to content

Commit 13b3b4c

Browse files
authored
Merge pull request #43 from uzumaxy/gh-pages
Updates from docs branch
2 parents 0bfa883 + 6654b15 commit 13b3b4c

File tree

5 files changed

+22
-391
lines changed

5 files changed

+22
-391
lines changed

README.rst

+2-384
Original file line numberDiff line numberDiff line change
@@ -4,391 +4,9 @@ pyvalid
44
.. image:: https://img.shields.io/codecov/c/github/uzumaxy/pyvalid.svg?style=plastic
55
.. image:: https://img.shields.io/github/workflow/status/uzumaxy/pyvalid/Python%20package?style=plastic
66

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.
3918

9+
Documentation: `uzumaxy.github.io/pyvalid <https://uzumaxy.github.io/pyvalid/>`_.
39210

39311
License
39412
+++++++

docs/assets/pyvalid-map.png

-37.2 KB
Binary file not shown.

0 commit comments

Comments
 (0)