Skip to content

Commit

Permalink
Fix misdetection of some 3.6 typing[_extensions] (#409)
Browse files Browse the repository at this point in the history

Co-authored-by: Olivier Grisel <olivier.grisel@ensta.org>
  • Loading branch information
marco-neumann-by and ogrisel committed Mar 1, 2021
1 parent fd596d2 commit 908ac8f
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Expand Up @@ -8,6 +8,10 @@ dev
https://www.python.org/dev/peps/pep-0563/
([PR #400](https://github.com/cloudpipe/cloudpickle/pull/400))

- Stricter parametrized type detection heuristics in
_is_parametrized_type_hint to limit false positives.
([PR #409](https://github.com/cloudpipe/cloudpickle/pull/409))

1.6.0
=====

Expand Down
26 changes: 21 additions & 5 deletions cloudpickle/cloudpickle.py
Expand Up @@ -455,15 +455,31 @@ def _extract_class_dict(cls):

if sys.version_info[:2] < (3, 7): # pragma: no branch
def _is_parametrized_type_hint(obj):
# This is very cheap but might generate false positives.
# This is very cheap but might generate false positives. So try to
# narrow it down is good as possible.
type_module = getattr(type(obj), '__module__', None)
from_typing_extensions = type_module == 'typing_extensions'
from_typing = type_module == 'typing'

# general typing Constructs
is_typing = getattr(obj, '__origin__', None) is not None

# typing_extensions.Literal
is_literal = getattr(obj, '__values__', None) is not None
is_literal = (
(getattr(obj, '__values__', None) is not None)
and from_typing_extensions
)

# typing_extensions.Final
is_final = getattr(obj, '__type__', None) is not None
is_final = (
(getattr(obj, '__type__', None) is not None)
and from_typing_extensions
)

# typing.ClassVar
is_classvar = (
(getattr(obj, '__type__', None) is not None) and from_typing
)

# typing.Union/Tuple for old Python 3.5
is_union = getattr(obj, '__union_params__', None) is not None
Expand All @@ -472,8 +488,8 @@ def _is_parametrized_type_hint(obj):
getattr(obj, '__result__', None) is not None and
getattr(obj, '__args__', None) is not None
)
return any((is_typing, is_literal, is_final, is_union, is_tuple,
is_callable))
return any((is_typing, is_literal, is_final, is_classvar, is_union,
is_tuple, is_callable))

def _create_parametrized_type_hint(origin, args):
return origin[args]
Expand Down
20 changes: 20 additions & 0 deletions tests/cloudpickle_test.py
Expand Up @@ -2301,6 +2301,26 @@ def reduce_myclass(x):
finally:
copyreg.dispatch_table.pop(MyClass)

def test_literal_misdetection(self):
# see https://github.com/cloudpipe/cloudpickle/issues/403
class MyClass:
@property
def __values__(self):
return ()

o = MyClass()
pickle_depickle(o, protocol=self.protocol)

def test_final_or_classvar_misdetection(self):
# see https://github.com/cloudpipe/cloudpickle/issues/403
class MyClass:
@property
def __type__(self):
return int

o = MyClass()
pickle_depickle(o, protocol=self.protocol)


class Protocol2CloudPickleTest(CloudPickleTest):

Expand Down

0 comments on commit 908ac8f

Please sign in to comment.