ingolemo / python-lenses

A python lens library for manipulating deeply nested immutable structures
GNU General Public License v3.0
308 stars 19 forks source link

Fix incompatibility with CPython 3.5.2 #19

Closed SnoopJ closed 5 years ago

SnoopJ commented 5 years ago

Hi, it looks like there's an incompatibility of lenses with CPython 3.5.2. This is the default Python3 on Ubuntu 16.04, so this bug represents a pretty large fraction of potential users of the library.

Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import lenses
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/snoopjedi/.local/lib/python3.5/site-packages/lenses/__init__.py", line 23, in <module>
    from . import optics
  File "/home/snoopjedi/.local/lib/python3.5/site-packages/lenses/optics/__init__.py", line 1, in <module>
    from .base import *
  File "/home/snoopjedi/.local/lib/python3.5/site-packages/lenses/optics/base.py", line 7, in <module>
    from ..maybe import Just, Nothing
  File "/home/snoopjedi/.local/lib/python3.5/site-packages/lenses/maybe.py", line 71, in <module>
    class Nothing(Just[A]):
  File "/usr/lib/python3.5/typing.py", line 1033, in __getitem__
    extra=self.__extra__)
  File "/usr/lib/python3.5/typing.py", line 909, in __new__
    self = super().__new__(cls, name, bases, namespace, _root=True)
  File "/usr/lib/python3.5/typing.py", line 107, in __new__
    return super().__new__(cls, name, bases, namespace)
  File "/usr/lib/python3.5/abc.py", line 133, in __new__
    cls = super().__new__(mcls, name, bases, namespace)
ValueError: 'item' in __slots__ conflicts with class variable

If I haven't done anything silly in installing this library, I think this is because of a bug in the typing module that causes populated __slots__ to be erroneously duplicated when using Generic. I'm not sure exactly what other versions this may affect (although I did try 3.3.7 and it worked fine), but it appears to have been fixed in 3.5.3.

This patch tests specifically for Python 3.5.2 and avoids declaring __slots__ in that case. The end result is that Just and subclasses may be slightly less performant (I haven't done any profiling), but the library will be usable, and it does not change the outcome of the test suite for 3.5.2:

GLOB sdist-make: /home/snoopjedi/python-lenses/setup.py
py35 inst-nodeps: /home/snoopjedi/python-lenses/.tox/.tmp/package/1/lenses-0.4.0.zip
WARNING: Discarding $PYTHONPATH from environment, to override specify PYTHONPATH in 'passenv' in your configuration.
py35 installed: atomicwrites==1.2.1,attrs==18.2.0,coverage==4.5.1,frozendict==1.2,hypothesis==3.75.4,lenses==0.4.0,more-itertools==4.3.0,mypy==0.630,mypy-extensions==0.4.1,pathlib2==2.3.2,pluggy==0.7.1,py==1.6.0,pyrsistent==0.14.4,pytest==3.8.2,pytest-cov==2.6.0,pytest-cover==3.0.0,pytest-coverage==0.0,singledispatch==3.4.0.3,six==1.11.0,typed-ast==1.1.0
py35 run-test-pre: PYTHONHASHSEED='2269748203'
py35 runtests: commands[0] | mypy -m lenses
py35 runtests: commands[1] | pytest lenses tests docs readme.rst '--doctest-glob=*.rst' --doctest-modules --cov
============================= test session starts ==============================
platform linux -- Python 3.5.2, pytest-3.8.2, py-1.6.0, pluggy-0.7.1
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/snoopjedi/python-lenses/.hypothesis/examples')
rootdir: /home/snoopjedi/python-lenses, inifile:
plugins: cov-2.6.0, hypothesis-3.75.4
collected 307 items

lenses/__init__.py .                                                     [  0%]
lenses/maybe.py .                                                        [  0%]
lenses/optics/base.py .............                                      [  4%]
lenses/optics/folds.py .                                                 [  5%]
lenses/optics/isomorphisms.py ...                                        [  6%]
lenses/optics/prisms.py ...                                              [  7%]
lenses/optics/setters.py .                                               [  7%]
lenses/optics/traversals.py ....                                         [  8%]
lenses/optics/true_lenses.py .......                                     [ 11%]
lenses/ui/__init__.py ...............                                    [ 15%]
lenses/ui/base.py .................................                      [ 26%]
tests/test_const.py ....                                                 [ 28%]
tests/test_frozendict.py ....                                            [ 29%]
tests/test_functorisor.py ...                                            [ 30%]
tests/test_hooks.py .....................                                [ 37%]
tests/test_identity.py ....                                              [ 38%]
tests/test_lens.py ..................................................... [ 55%]
...............                                                          [ 60%]
tests/test_maybe.py .....................                                [ 67%]
tests/test_optics.py ................................................... [ 84%]
...........................                                              [ 92%]
tests/test_pyrsistent.py ........                                        [ 95%]
tests/test_typeclass_laws.py .........                                   [ 98%]
docs/tutorial/compose.rst .                                              [ 98%]
docs/tutorial/intro.rst .                                                [ 99%]
docs/tutorial/methods.rst .                                              [ 99%]
docs/tutorial/optics.rst .                                               [ 99%]
readme.rst .                                                             [100%]

----------- coverage: platform linux, python 3.5.2-final-0 -----------
Name                          Stmts   Miss Branch BrPart  Cover   Missing
-------------------------------------------------------------------------
lenses/hooks/__init__.py          9      2      2      0    82%   16-17
lenses/hooks/hook_funcs.py      122     15     18      4    86%   39, 97-109, 351-359, 397-398, 36->39, 80->97, 338->351, 393->397
lenses/maybe.py                  65      1     10      1    97%   31, 30->31
lenses/optics/base.py           225      1     82      2    99%   224, 223->224, 227->exit
lenses/optics/traversals.py     131      1     38      1    99%   173, 170->173
lenses/typeclass.py              53      6     16      2    88%   13, 30-38, 63, 28->30, 62->63
-------------------------------------------------------------------------
TOTAL                          1132     26    240     10    97%

15 files skipped due to complete coverage.

Required test coverage of 95% reached. Total coverage: 97.38%

========================= 307 passed in 13.05 seconds ==========================
___________________________________ summary ____________________________________
  py35: commands succeeded
  congratulations :)
codecov[bot] commented 5 years ago

Codecov Report

Merging #19 into master will decrease coverage by 0.08%. The diff coverage is 66.66%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #19      +/-   ##
==========================================
- Coverage   98.84%   98.76%   -0.09%     
==========================================
  Files          21       21              
  Lines        1130     1132       +2     
  Branches      119      120       +1     
==========================================
+ Hits         1117     1118       +1     
  Misses          8        8              
- Partials        5        6       +1
Impacted Files Coverage Δ
lenses/maybe.py 98.46% <66.66%> (-1.54%) :arrow_down:

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 1343896...c14a2a5. Read the comment docs.