keleshev / schema

Schema validation just got Pythonic
MIT License
2.86k stars 214 forks source link

Incompatibility with Python 2.7 #299

Open juliendelplanque opened 11 months ago

juliendelplanque commented 11 months ago

Hello,

As part of a coding dojo we organized in our team, we took the opportunity to see what could be improved in this library (which is really nice by the way!).

During this session, we have observed that the tox.ini file specifies that the library is compatible with Python 2.7. However, it seems that test fail when running them in py27 tox environment.

Looks like _invoke_with_optional_kwargs function uses a function from inspect module that is not available in Python 2.7.

Is that expected? Is the library still aiming to support Python 2 ?


tox run -e py27
py27: remove tox env folder /home/jude/Documents/GIT/schema-copy/.tox/py27
.pkg: remove tox env folder /home/jude/Documents/GIT/schema-copy/.tox/.pkg
py27: install_deps> python -E -m pip install mock pytest
.pkg: install_requires> python -I -m pip install 'setuptools>=40.8.0' wheel
.pkg: _optional_hooks> python /usr/local/lib/python3.10/dist-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: get_requires_for_build_sdist> python /usr/local/lib/python3.10/dist-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: prepare_metadata_for_build_wheel> python /usr/local/lib/python3.10/dist-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: build_sdist> python /usr/local/lib/python3.10/dist-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
py27: install_package_deps> python -E -m pip install 'contextlib2>=0.5.5'
py27: install_package> python -E -m pip install --force-reinstall --no-deps /home/jude/Documents/GIT/schema-copy/.tox/.tmp/package/80/schema-0.7.5.tar.gz
py27: commands[0]> py.test
============================= test session starts ==============================
platform linux2 -- Python 2.7.18, pytest-4.6.11, py-1.11.0, pluggy-0.13.1
cachedir: .tox/py27/.pytest_cache
rootdir: /home/jude/Documents/GIT/schema-copy
collected 116 items                                                            

test_schema.py .........................................FFF............. [ 49%]
.........FF................................................              [100%]

=================================== FAILURES ===================================
_____ test_optional_callable_default_get_inherited_schema_validate_kwargs ______

    def test_optional_callable_default_get_inherited_schema_validate_kwargs():
        def convert(data, increment):
            if isinstance(data, int):
                return data + increment
            return data

        s = {"k": int, "d": {Optional("k", default=lambda **kw: convert(2, kw['increment'])): int, "l": [{"l": [int]}]}}
        v = {"k": 1, "d": {"l": [{"l": [3, 4, 5]}]}}
>       d = Schema(s).validate(v, increment=1)

test_schema.py:752: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
schema.py:405: in validate
    nvalue = Schema(svalue, error=e, ignore_extra_keys=i).validate(value, **kwargs)
schema.py:431: in validate
    new[default.key] = _invoke_with_optional_kwargs(default.default, **kwargs) if callable(default.default) else default.default
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

f = <function <lambda> at 0x7f99b8a291d0>, kwargs = {'increment': 1}

    def _invoke_with_optional_kwargs(f, **kwargs):
>       s = inspect.signature(f)
E       AttributeError: 'module' object has no attribute 'signature'

schema.py:277: AttributeError
____ test_optional_callable_default_ignore_inherited_schema_validate_kwargs ____

    def test_optional_callable_default_ignore_inherited_schema_validate_kwargs():

        def convert(data, increment):
            if isinstance(data, int):
                return data + increment
            return data

        s = {"k": int, "d": {Optional("k", default=lambda: 42): int, "l": [{"l": [int]}]}}
        v = {"k": 1, "d": {"l": [{"l": [3, 4, 5]}]}}
>       d = Schema(s).validate(v, increment=1)

test_schema.py:767: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
schema.py:405: in validate
    nvalue = Schema(svalue, error=e, ignore_extra_keys=i).validate(value, **kwargs)
schema.py:431: in validate
    new[default.key] = _invoke_with_optional_kwargs(default.default, **kwargs) if callable(default.default) else default.default
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

f = <function <lambda> at 0x7f99b92393d0>, kwargs = {'increment': 1}

    def _invoke_with_optional_kwargs(f, **kwargs):
>       s = inspect.signature(f)
E       AttributeError: 'module' object has no attribute 'signature'

schema.py:277: AttributeError
__________________________ test_inheritance_optional ___________________________

    def test_inheritance_optional():
        def convert(data, increment):
            if isinstance(data, int):
                return data + increment
            return data

        class MyOptional(Optional):

            """This overrides the default property so it increments according
            to kwargs passed to validate()
            """
            @property
            def default(self):

                def wrapper(**kwargs):
                    if 'increment' in kwargs:
                        return convert(self._default, kwargs['increment'])
                    return self._default
                return wrapper

            @default.setter
            def default(self, value):
                self._default = value

        s = {"k": int, "d": {MyOptional("k", default=2): int, "l": [{"l": [int]}]}}
        v = {"k": 1, "d": {"l": [{"l": [3, 4, 5]}]}}
>       d = Schema(s).validate(v, increment=1)

test_schema.py:799: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
schema.py:405: in validate
    nvalue = Schema(svalue, error=e, ignore_extra_keys=i).validate(value, **kwargs)
schema.py:431: in validate
    new[default.key] = _invoke_with_optional_kwargs(default.default, **kwargs) if callable(default.default) else default.default
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

f = <function wrapper at 0x7f99b9239650>, kwargs = {'increment': 1}

    def _invoke_with_optional_kwargs(f, **kwargs):
>       s = inspect.signature(f)
E       AttributeError: 'module' object has no attribute 'signature'

schema.py:277: AttributeError
_____________________ test_json_schema_default_is_callable _____________________

    def test_json_schema_default_is_callable():
        def default_func():
            return 'Hello!'
        s = Schema({Optional("test", default=default_func): str})
>       assert s.json_schema("my-id") == {
            "$schema": "http://json-schema.org/draft-07/schema#",
            "$id": "my-id",
            "properties": {"test": {"default": "Hello!", "type": "string"}},
            "required": [],
            "additionalProperties": False,
            "type": "object",
        }

test_schema.py:1117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
schema.py:693: in json_schema
    return _json_schema(self, True)
schema.py:662: in _json_schema
    expanded_schema[key_name]["default"] = _to_json_type(_invoke_with_optional_kwargs(key.default, **kwargs) if callable(key.default) else key.default)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

f = <function default_func at 0x7f99b9239c50>, kwargs = {}

    def _invoke_with_optional_kwargs(f, **kwargs):
>       s = inspect.signature(f)
E       AttributeError: 'module' object has no attribute 'signature'

schema.py:277: AttributeError
____ test_json_schema_default_is_callable_with_args_passed_from_json_schema ____

    def test_json_schema_default_is_callable_with_args_passed_from_json_schema():
        def default_func(**kwargs):
            return 'Hello, ' + kwargs['name']
        s = Schema({Optional("test", default=default_func): str})
>       assert s.json_schema("my-id", name='World!') == {
            "$schema": "http://json-schema.org/draft-07/schema#",
            "$id": "my-id",
            "properties": {"test": {"default": "Hello, World!", "type": "string"}},
            "required": [],
            "additionalProperties": False,
            "type": "object",
        }

test_schema.py:1131: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
schema.py:693: in json_schema
    return _json_schema(self, True)
schema.py:662: in _json_schema
    expanded_schema[key_name]["default"] = _to_json_type(_invoke_with_optional_kwargs(key.default, **kwargs) if callable(key.default) else key.default)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

f = <function default_func at 0x7f99b9239a50>, kwargs = {'name': 'World!'}

    def _invoke_with_optional_kwargs(f, **kwargs):
>       s = inspect.signature(f)
E       AttributeError: 'module' object has no attribute 'signature'

schema.py:277: AttributeError
===================== 5 failed, 111 passed in 0.47 seconds =====================
py27: exit 1 (0.60 seconds) /home/jude/Documents/GIT/schema-copy> py.test pid=324489
.pkg: _exit> python /usr/local/lib/python3.10/dist-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
  py27: FAIL code 1 (5.14=setup[4.54]+cmd[0.60] seconds)
  evaluation failed :( (5.16 seconds)
  ``
skorokithakis commented 11 months ago

I doubt that we want to put any effort into supporting 2.7, it's 13 years old at this point. We should just remove the claim, really.

juliendelplanque commented 11 months ago

I would agree. Also, it allows to remove Python 2 calls to super().