[go: up one dir, main page]

File: validate.rst

package info (click to toggle)
streamlink 7.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 5,428 kB
  • sloc: python: 49,104; sh: 184; makefile: 145
file content (259 lines) | stat: -rw-r--r-- 11,524 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
Validation schemas
------------------

.. ..
    Sphinx's autodoc doesn't properly document imported module members and it just outputs "alias of" for re-exported classes.
    This means we'll have to run `automodule` twice if we want to document the original classes:
    1. the main public interface (which contains aliases, like `all` for `AllSchema` for example)
    2. the original schema classes with their full signatures and docstrings
    .
    Ignore unneeded classes like `SchemaContainer` which are not useful for the API docs.
    .
    Ignore `validate` as well, as `functools.singledispatch` functions are not fully supported by autodoc.
    Instead, manually document `validate` and its overloading functions for base schema types here at the top,
    just below the manually imported `Schema` (the main validation schema interface).
    The documentations for any custom schemas like `AllSchema` for example is done on the schemas themselves.
    .
    Ideally, we'd just run autodoc on the main module and configure the order of items. :(

Please see the :ref:`validation schema guides <api_guide/validate:Validation schemas>`
for an introduction to this API and a list of examples.

.. admonition:: Public interface
   :class: caution

   While the internals are implemented in the ``streamlink.validate`` package,
   :ref:`streamlink.plugin.api.validate <api/validate:Validation schemas>` provides the main public interface
   for plugin implementors.

.. autoclass:: streamlink.plugin.api.validate.Schema
    :members:
    :undoc-members:

.. py:function:: validate(schema, value)
    :module: streamlink.plugin.api.validate

    The core of the :mod:`streamlink.plugin.api.validate` module.

    It validates the given input ``value`` and returns a value according to the specific validation rules of the ``schema``.

    If the validation fails, a :exc:`ValidationError <_exception.ValidationError>` is raised with a detailed error message.

    The ``schema`` can be any kind of object. Depending on the ``schema``, different validation rules apply.

    Simple schema objects like ``"abc"`` or ``123`` for example test the equality of ``value`` and ``schema``
    and return ``value`` again, while type schema objects like ``str`` test whether ``value`` is an instance of ``schema``.
    ``schema`` objects which are callable receive ``value`` as a single argument and must return a truthy value, otherwise the
    validation fails. These are just a few examples.

    The ``validate`` module implements lots of special schemas, like :class:`validate.all <all>` or :class:`validate.any <any>`
    for example, which are schema containers that receive a sequence of sub-schemas as arguments and where each sub-schema
    then gets validated one after another.

    :class:`validate.all <all>` requires each sub-schema to successfully validate. It passes the return value of each
    sub-schema to the next one and then returns the return value of the last sub-schema.

    :class:`validate.any <any>` on the other hand requires at least one sub-schema to be valid and returns the return value of
    the first valid sub-schema. Any validation failures prior will be ignored, but at least one must succeed.

    Other special ``schema`` cases for example are instances of sequences like ``list`` or ``tuple``, or mappings like ``dict``.
    Here, each sequence item or key-value mapping pair is validated against the input ``value``
    and a new sequence/mapping object according to the ``schema`` validation is returned.

    :func:`validate()` should usually not be called directly when validating schemas. Instead, the wrapper method
    :meth:`Schema.validate() <Schema.validate>` of the main :class:`Schema` class should be called. Other Streamlink APIs
    like the methods of the :class:`HTTPSession <streamlink.session.Streamlink.http>` or the various
    :mod:`streamlink.utils.parse` functions for example expect this interface when the ``schema`` keyword is set,
    which allows for immediate validation of the data using a :class:`Schema` object.

    :func:`validate()` is implemented using the stdlib's :func:`functools.singledispatch` decorator, where more specific
    schemas overload the default implementation with more validation logic.

    ----

    By default, :func:`validate()` compares ``value`` and ``schema`` for equality. This means that simple schema objects
    like booleans, strings, numbers, None, etc. are validated here, as well as anything unknown.

    Example:

    .. code-block:: python

        schema = validate.Schema(123)
        assert schema.validate(123) == 123
        assert schema.validate(123.0) == 123.0
        schema.validate(456)  # raises ValidationError
        schema.validate(None)  # raises ValidationError

    :param Any schema: Any kind of object not handled by a more specific validation function
    :param Any value: The input value
    :raise ValidationError: If ``value`` and ``schema`` are not equal
    :return: Unmodified ``value``

.. py:function:: _validate_type(schema, value)
    :module: streamlink.plugin.api.validate

    :class:`type` validation.

    Checks if ``value`` is an instance of ``schema``.

    Example:

    .. code-block:: python

        schema = validate.Schema(int)
        assert schema.validate(123) == 123
        assert schema.validate(True) is True  # `bool` is a subclass of `int`
        schema.validate("123")  # raises ValidationError

    *This function is included for documentation purposes only! (singledispatch overload)*

    :param type schema: A :class:`type` object
    :param Any value: The input value
    :raise ValidationError: If ``value`` is not an instance of ``schema``
    :return: Unmodified ``value``

.. py:function:: _validate_callable(schema, value)
    :module: streamlink.plugin.api.validate

    ``Callable`` validation.

    Validates a ``schema`` function where ``value`` gets passed as a single argument.

    Must return a truthy value.

    Example:

    .. code-block:: python

        schema = validate.Schema(
            lambda val: val < 2,
        )
        assert schema.validate(1) == 1
        schema.validate(2)  # raises ValidationError

    *This function is included for documentation purposes only! (singledispatch overload)*

    :param Callable schema: A function with one argument
    :param Any value: The input value
    :raise ValidationError: If ``schema`` returns a non-truthy value
    :return: Unmodified ``value``

.. py:function:: _validate_sequence(schema, value)
    :module: streamlink.plugin.api.validate

    :class:`list <builtins.list>`, :class:`tuple`, :class:`set` and :class:`frozenset` validation.

    Each item of ``value`` gets validated against **any** of the items of ``schema``.

    Please note the difference between :class:`list <builtins.list>`
    and the :class:`ListSchema <_schemas.ListSchema>` validation.

    Example:

    .. code-block:: python

        schema = validate.Schema([1, 2, 3])
        assert schema.validate([]) == []
        assert schema.validate([1, 2]) == [1, 2]
        assert schema.validate([3, 2, 1]) == [3, 2, 1]
        schema.validate({1, 2, 3})  # raises ValidationError
        schema.validate([1, 2, 3, 4])  # raises ValidationError

    *This function is included for documentation purposes only! (singledispatch overload)*

    :param Union[list, tuple, set, frozenset] schema: A sequence of validation schemas
    :param Any value: The input value
    :raise ValidationError: If ``value`` is not an instance of the ``schema``'s own type
    :return: A new sequence of the same type as ``schema`` with each item of ``value`` being validated

.. py:function:: _validate_dict(schema, value)
    :module: streamlink.plugin.api.validate

    :class:`dict` validation.

    Each key-value pair of ``schema`` gets validated against the respective key-value pair of ``value``.

    Additional keys in ``value`` are ignored and not included in the validation result.

    If a ``schema`` key is an instance of :class:`OptionalSchema <_schemas.OptionalSchema>`, then ``value`` may omit it.

    If one of the ``schema``'s keys is a :class:`type`,
    :class:`AllSchema <_schemas.AllSchema>`, :class:`AnySchema <_schemas.AnySchema>`,
    :class:`TransformSchema <_schemas.TransformSchema>`, or :class:`UnionSchema <_schemas.UnionSchema>`,
    then all key-value pairs of ``value`` are validated against the ``schema``'s key-value pair.

    Example:

    .. code-block:: python

        schema = validate.Schema({
            "key": str,
            validate.optional("opt"): 123,
        })
        assert schema.validate({"key": "val", "other": 123}) == {"key": "val"}
        assert schema.validate({"key": "val", "opt": 123}) == {"key": "val", "opt": 123}
        schema.validate(None)  # raises ValidationError
        schema.validate({})  # raises ValidationError
        schema.validate({"key": 123})  # raises ValidationError
        schema.validate({"key": "val", "opt": 456})  # raises ValidationError

    .. code-block:: python

        schema = validate.Schema({
            validate.any("a", "b"): int,
        })
        assert schema.validate({}) == {}
        assert schema.validate({"a": 1}) == {"a": 1}
        assert schema.validate({"b": 2}) == {"b": 2}
        assert schema.validate({"a": 1, "b": 2}) == {"a": 1, "b": 2}
        schema.validate({"a": 1, "b": 2, "other": 0})  # raises ValidationError
        schema.validate({"a": None})  # raises ValidationError

    *This function is included for documentation purposes only! (singledispatch overload)*

    :param dict schema: A :class:`dict`
    :param Any value: The input value
    :raise ValidationError: If ``value`` is not a :class:`dict`
    :raise ValidationError: If any of the ``schema``'s non-optional keys are not part of the input ``value``
    :return: A new :class:`dict`

.. py:function:: _validate_pattern(schema, value)
    :module: streamlink.plugin.api.validate

    :class:`re.Pattern` validation.

    Calls the :meth:`re.Pattern.search()` method on the ``schema`` pattern.

    Please note the difference between :class:`re.Pattern` and the :class:`RegexSchema <_schemas.RegexSchema>` validation.

    Example:

    .. code-block:: python

        schema = validate.Schema(
            re.compile(r"^Hello, (?P<name>\w+)!$"),
        )
        assert schema.validate("Does not match") is None
        assert schema.validate("Hello, world!")["name"] == "world"
        schema.validate(123)  # raises ValidationError
        schema.validate(b"Hello, world!")  # raises ValidationError

    *This function is included for documentation purposes only! (singledispatch overload)*

    :param re.Pattern schema: A compiled :class:`re.Pattern` object (:func:`re.compile()` return value)
    :param Any value: The input value
    :raise ValidationError: If ``value`` is not an instance of :class:`str` or :class:`bytes`
    :raise ValidationError: If the type of ``value`` doesn't match ``schema``'s :class:`str`/:class:`bytes` type
    :return: ``None`` if ``value`` doesn't match ``schema``, or the resulting :class:`re.Match` object

.. automodule:: streamlink.plugin.api.validate
    :imported-members:
    :exclude-members: Schema, SchemaContainer, validate
    :member-order: bysource

.. automodule:: streamlink.validate._schemas
    :exclude-members: SchemaContainer
    :member-order: bysource
    :no-show-inheritance:

.. autoexception:: streamlink.validate._exception.ValidationError