Skip to content

Commit

Permalink
Merge pull request #174 from netromdk/issue-159
Browse files Browse the repository at this point in the history
Union types detection also considers attributes
  • Loading branch information
netromdk committed Apr 7, 2023
2 parents 53ef3a2 + 48d3c4d commit f8235dc
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 9 deletions.
31 changes: 31 additions & 0 deletions tests/lang.py
Expand Up @@ -1939,6 +1939,37 @@ class Bar: pass
self.assertFalse(visitor.union_types())

if current_version() >= (3, 10):
# Issue 159: Attributes can also be used for union types.
visitor = self.visit("""
def function(argument: ipaddress.IPv4Interface | ipaddress.IPv6Interface):
if isinstance(argument, ipaddress.IPv4Address):
print("We got an IPv4")
elif isinstance(argument, ipaddress.IPv6Address):
print("We got an IPv6")
else:
print(f"We got type {type(argument)}")
""")
self.assertTrue(visitor.union_types())
self.assertOnlyIn((3, 10), visitor.minimum_versions())
visitor = self.visit("""
def function(argument: ipaddress.IPv4Interface | None):
pass
""")
self.assertTrue(visitor.union_types())
self.assertOnlyIn((3, 10), visitor.minimum_versions())
visitor = self.visit("""
def function(argument: None | ipaddress.IPv4Interface):
pass
""")
self.assertTrue(visitor.union_types())
self.assertOnlyIn((3, 10), visitor.minimum_versions())
visitor = self.visit("""
def function(argument: str | ipaddress.IPv4Interface):
pass
""")
self.assertTrue(visitor.union_types())
self.assertOnlyIn((3, 10), visitor.minimum_versions())

# Issue 109: When not evaluating annotations, returns annotations must not be visited.
self.config.set_eval_annotations(False)
visitor = self.visit("""
Expand Down
29 changes: 20 additions & 9 deletions vermin/source_visitor.py
Expand Up @@ -1386,10 +1386,16 @@ def has_du():
has_du()

def is_none_or_name(node):
return is_none_node(node) or \
(isinstance(node, ast.Name) and
((node.id not in self.__name_res and node.id not in self.__user_defs) or
(node.id in self.__name_res_type)))
if is_none_node(node):
return True
name = None
if isinstance(node, ast.Name):
name = node.id
elif isinstance(node, ast.Attribute):
name = dotted_name(self.__get_attribute_name(node))
return name is not None and \
((name not in self.__name_res and name not in self.__user_defs) or
(name in self.__name_res_type))
if is_none_or_name(node.left) and is_none_or_name(node.right):
self.__union_types = True
self.__vvprint("union types as `X | Y`", line=node.lineno, versions=[None, (3, 10)],
Expand Down Expand Up @@ -1674,13 +1680,18 @@ def visit_AugAssign(self, node):
# |=
if isinstance(node.op, ast.BitOr):
def is_union_type(node):
return isinstance(node, ast.Name) and \
((node.id not in self.__name_res and node.id not in self.__user_defs) or
name = None
if isinstance(node, ast.Name):
name = node.id
elif isinstance(node, ast.Attribute):
name = dotted_name(self.__get_attribute_name(node))
return name is not None and \
((name not in self.__name_res and name not in self.__user_defs) or
# AugAssign: both variable names are the same so require two types.
(node.id in self.__name_res_type and len(self.__name_res_type[node.id]) > 1) or
(name in self.__name_res_type and len(self.__name_res_type[name]) > 1) or
# Unless it's the target value, then require not name res, is user def and res type.
(node.id not in self.__name_res and node.id in self.__user_defs and \
node.id in self.__name_res_type))
(name not in self.__name_res and name in self.__user_defs and \
name in self.__name_res_type))

# Example:
# AugAssign(target=Name(id='d', ctx=Store()),
Expand Down

0 comments on commit f8235dc

Please sign in to comment.