Repository: numpy/numpy
Pull request: MAINT Use set litterals
Since Python 2.6 is no longer supported, set literals can be used which are a bit more readable, and [somewhat faster](https://renzo.lucioni.xyz/pythons-set-literals/) (though I don't thing that it matters much for the below changes).

Closes https://github.com/numpy/numpy/issues/3084

(I have not run 2to3 `setliteral` fixer as suggested in the issue, but rather did the changes manually).

Problem statement:
2to3 run `setliteral` fixer after 2.6 support is dropped.
Set literals are in not available in Python 2.6 and Numpy will likely continue support for a number of years. The set literals are nice, but not needed, the current syntax in Numpy is forward portable. So mark this as a task but attach it to no milestone.

Hints from issue discussion:

Commit message: Use set litterals

Patch to review:
diff --git a/numpy/core/_type_aliases.py b/numpy/core/_type_aliases.py
index cce6c0425271..d6e1a1fb732c 100644
--- a/numpy/core/_type_aliases.py
+++ b/numpy/core/_type_aliases.py
@@ -60,7 +60,7 @@ def get(self, key, default=None):
     else:
         _concrete_typeinfo[k] = v
 
-_concrete_types = set(v.type for k, v in _concrete_typeinfo.items())
+_concrete_types = {v.type for k, v in _concrete_typeinfo.items()}
 
 
 def _bits_of(obj):
diff --git a/numpy/core/einsumfunc.py b/numpy/core/einsumfunc.py
index d9f88cb1c0e1..e863c1f2e859 100644
--- a/numpy/core/einsumfunc.py
+++ b/numpy/core/einsumfunc.py
@@ -169,7 +169,7 @@ def _optimal_path(input_sets, output_set, idx_dict, memory_limit):
     Examples
     --------
     >>> isets = [set('abd'), set('ac'), set('bdc')]
-    >>> oset = set('')
+    >>> oset = {''}
     >>> idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4}
     >>> _path__optimal_path(isets, oset, idx_sizes, 5000)
     [(0, 2), (0, 1)]
@@ -340,7 +340,7 @@ def _greedy_path(input_sets, output_set, idx_dict, memory_limit):
     Examples
     --------
     >>> isets = [set('abd'), set('ac'), set('bdc')]
-    >>> oset = set('')
+    >>> oset = {''}
     >>> idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4}
     >>> _path__greedy_path(isets, oset, idx_sizes, 5000)
     [(0, 2), (0, 1)]
@@ -442,11 +442,11 @@ def _can_dot(inputs, result, idx_removed):
     --------
 
     # Standard GEMM operation
-    >>> _can_dot(['ij', 'jk'], 'ik', set('j'))
+    >>> _can_dot(['ij', 'jk'], 'ik', {'j'})
     True
 
     # Can use the standard BLAS, but requires odd data movement
-    >>> _can_dot(['ijj', 'jk'], 'ik', set('j'))
+    >>> _can_dot(['ijj', 'jk'], 'ik', {'j'})
     False
 
     # DDOT where the memory is not aligned
diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py
index aa5be1af3c57..85f45c795e39 100644
--- a/numpy/core/numeric.py
+++ b/numpy/core/numeric.py
@@ -745,7 +745,7 @@ def require(a, dtype=None, requirements=None):
     if not requirements:
         return asanyarray(a, dtype=dtype)
     else:
-        requirements = set(possible_flags[x.upper()] for x in requirements)
+        requirements = {possible_flags[x.upper()] for x in requirements}
 
     if 'E' in requirements:
         requirements.remove('E')
@@ -754,7 +754,7 @@ def require(a, dtype=None, requirements=None):
         subok = True
 
     order = 'A'
-    if requirements >= set(['C', 'F']):
+    if requirements >= {'C', 'F'}:
         raise ValueError('Cannot specify both "C" and "F" order')
     elif 'F' in requirements:
         order = 'F'
diff --git a/numpy/core/shape_base.py b/numpy/core/shape_base.py
index 6d234e527853..a529d2ad74c3 100644
--- a/numpy/core/shape_base.py
+++ b/numpy/core/shape_base.py
@@ -410,7 +410,7 @@ def stack(arrays, axis=0, out=None):
     if not arrays:
         raise ValueError('need at least one array to stack')
 
-    shapes = set(arr.shape for arr in arrays)
+    shapes = {arr.shape for arr in arrays}
     if len(shapes) != 1:
         raise ValueError('all input arrays must have the same shape')
 
diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py
index 8cde19612da7..c55751e3cf38 100644
--- a/numpy/core/tests/test_dtype.py
+++ b/numpy/core/tests/test_dtype.py
@@ -156,9 +156,9 @@ def test_not_lists(self):
         the dtype constructor.
         """
         assert_raises(TypeError, np.dtype,
-                      dict(names=set(['A', 'B']), formats=['f8', 'i4']))
+                      dict(names={'A', 'B'}, formats=['f8', 'i4']))
         assert_raises(TypeError, np.dtype,
-                      dict(names=['A', 'B'], formats=set(['f8', 'i4'])))
+                      dict(names=['A', 'B'], formats={'f8', 'i4'}))
 
     def test_aligned_size(self):
         # Check that structured dtypes get padded to an aligned size
diff --git a/numpy/core/tests/test_scalarmath.py b/numpy/core/tests/test_scalarmath.py
index a55f06b693bd..423e437f11ff 100644
--- a/numpy/core/tests/test_scalarmath.py
+++ b/numpy/core/tests/test_scalarmath.py
@@ -565,10 +565,10 @@ def test_seq_repeat(self):
         # Some of this behaviour may be controversial and could be open for
         # change.
         accepted_types = set(np.typecodes["AllInteger"])
-        deprecated_types = set('?')
+        deprecated_types = {'?'}
         forbidden_types = (
             set(np.typecodes["All"]) - accepted_types - deprecated_types)
-        forbidden_types -= set('V')  # can't default-construct void scalars
+        forbidden_types -= {'V'}  # can't default-construct void scalars
 
         for seq_type in (list, tuple):
             seq = seq_type([1, 2, 3])
diff --git a/numpy/distutils/command/build_ext.py b/numpy/distutils/command/build_ext.py
index 18d36480ac25..ab9d585a5558 100644
--- a/numpy/distutils/command/build_ext.py
+++ b/numpy/distutils/command/build_ext.py
@@ -265,10 +265,10 @@ def run(self):
         # we blindly assume that both packages need all of the libraries,
         # resulting in a larger wheel than is required. This should be fixed,
         # but it's so rare that I won't bother to handle it.
-        pkg_roots = set(
+        pkg_roots = {
             self.get_ext_fullname(ext.name).split('.')[0]
             for ext in self.extensions
-        )
+        }
         for pkg_root in pkg_roots:
             shared_lib_dir = os.path.join(pkg_root, '.libs')
             if not self.inplace:
diff --git a/numpy/lib/_iotools.py b/numpy/lib/_iotools.py
index b604b8c52de3..d5d990a9a02f 100644
--- a/numpy/lib/_iotools.py
+++ b/numpy/lib/_iotools.py
@@ -693,7 +693,7 @@ def __init__(self, dtype_or_func=None, default=None, missing_values=None,
                     self.func = lambda x: int(float(x))
         # Store the list of strings corresponding to missing values.
         if missing_values is None:
-            self.missing_values = set([''])
+            self.missing_values = {''}
         else:
             if isinstance(missing_values, basestring):
                 missing_values = missing_values.split(",")
diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py
index f623c58e7cf5..db6a8e5eb2c9 100644
--- a/numpy/lib/npyio.py
+++ b/numpy/lib/npyio.py
@@ -2126,10 +2126,10 @@ def encode_unicode_cols(row_tup):
 
         if names is None:
             # If the dtype is uniform (before sizing strings)
-            base = set([
+            base = {
                 c_type
                 for c, c_type in zip(converters, column_types)
-                if c._checked])
+                if c._checked}
             if len(base) == 1:
                 uniform_type, = base
                 (ddtype, mdtype) = (uniform_type, bool)
diff --git a/numpy/lib/tests/test__iotools.py b/numpy/lib/tests/test__iotools.py
index b4888f1bd732..2d090709a82b 100644
--- a/numpy/lib/tests/test__iotools.py
+++ b/numpy/lib/tests/test__iotools.py
@@ -246,7 +246,7 @@ def test_keep_missing_values(self):
         converter = StringConverter(int, default=0,
                                     missing_values="N/A")
         assert_equal(
-            converter.missing_values, set(['', 'N/A']))
+            converter.missing_values, {'', 'N/A'})
 
     def test_int64_dtype(self):
         "Check that int64 integer types can be specified"
diff --git a/numpy/lib/tests/test_shape_base.py b/numpy/lib/tests/test_shape_base.py
index e338467f9fb6..01ea028bbf62 100644
--- a/numpy/lib/tests/test_shape_base.py
+++ b/numpy/lib/tests/test_shape_base.py
@@ -260,8 +260,8 @@ def empty_to_1(x):
     def test_with_iterable_object(self):
         # from issue 5248
         d = np.array([
-            [set([1, 11]), set([2, 22]), set([3, 33])],
-            [set([4, 44]), set([5, 55]), set([6, 66])]
+            [{1, 11}, {2, 22}, {3, 33}],
+            [{4, 44}, {5, 55}, {6, 66}]
         ])
         actual = np.apply_along_axis(lambda a: set.union(*a), 0, d)
         expected = np.array([{1, 11, 4, 44}, {2, 22, 5, 55}, {3, 33, 6, 66}])
diff --git a/numpy/testing/_private/parameterized.py b/numpy/testing/_private/parameterized.py
index 53e67517dd5a..a5fa4fb5e287 100644
--- a/numpy/testing/_private/parameterized.py
+++ b/numpy/testing/_private/parameterized.py
@@ -190,7 +190,7 @@ def parameterized_argument_value_pairs(func, p):
         in zip(named_args, argspec.defaults or [])
     ])
 
-    seen_arg_names = set([ n for (n, _) in result ])
+    seen_arg_names = {n for (n, _) in result}
     keywords = QuietOrderedDict(sorted([
         (name, p.kwargs[name])
         for name in p.kwargs


Code review comments:
diff_hunk: @@ -169,7 +169,7 @@ def _optimal_path(input_sets, output_set, idx_dict, memory_limit):
     Examples
     --------
     >>> isets = [set('abd'), set('ac'), set('bdc')]
-    >>> oset = set('')
+    >>> oset = {''}
path: numpy/core/einsumfunc.py
text: @user1:
This change is not correct - `set('')` is the empty set, not a set containing the empty string. This is better to leave unchanged, so it matches the `set('ac')` above

diff_hunk: @@ -442,11 +442,11 @@ def _can_dot(inputs, result, idx_removed):
     --------
 
     # Standard GEMM operation
-    >>> _can_dot(['ij', 'jk'], 'ik', set('j'))
+    >>> _can_dot(['ij', 'jk'], 'ik', {'j'})
     True
 
     # Can use the standard BLAS, but requires odd data movement
-    >>> _can_dot(['ijj', 'jk'], 'ik', set('j'))
+    >>> _can_dot(['ijj', 'jk'], 'ik', {'j'})
path: numpy/core/einsumfunc.py
text: @user1:
Better not to change this oen either, since as it was it matches the one on line 453