Python: make ruff & black happy
[platform/upstream/libxkbcommon.git] / scripts / perfect_hash.py
index 95c6156..a017bb6 100644 (file)
@@ -92,7 +92,7 @@ else:
     from io import StringIO
 
 
     from io import StringIO
 
 
-__version__ = '0.4.2'
+__version__ = "0.4.2"
 
 
 verbose = False
 
 
 verbose = False
@@ -107,8 +107,9 @@ class Graph(object):
     are assigned such that the two values corresponding to an edge add up to
     the desired edge value (mod N).
     """
     are assigned such that the two values corresponding to an edge add up to
     the desired edge value (mod N).
     """
+
     def __init__(self, N):
     def __init__(self, N):
-        self.N = N                     # number of vertices
+        self.N = N  # number of vertices
 
         # maps a vertex number to the list of tuples (vertex, edge value)
         # to which it is connected by edges.
 
         # maps a vertex number to the list of tuples (vertex, edge value)
         # to which it is connected by edges.
@@ -145,7 +146,7 @@ class Graph(object):
                 continue
 
             # explore tree starting at 'root'
                 continue
 
             # explore tree starting at 'root'
-            self.vertex_values[root] = 0    # set arbitrarily to zero
+            self.vertex_values[root] = 0  # set arbitrarily to zero
 
             # Stack of vertices to visit, a list of tuples (parent, vertex)
             tovisit = [(None, root)]
 
             # Stack of vertices to visit, a list of tuples (parent, vertex)
             tovisit = [(None, root)]
@@ -170,7 +171,8 @@ class Graph(object):
                     # Set new vertex's value to the desired edge value,
                     # minus the value of the vertex we came here from.
                     self.vertex_values[neighbor] = (
                     # Set new vertex's value to the desired edge value,
                     # minus the value of the vertex we came here from.
                     self.vertex_values[neighbor] = (
-                        edge_value - self.vertex_values[vertex]) % self.N
+                        edge_value - self.vertex_values[vertex]
+                    ) % self.N
 
         # check if all vertices have a valid value
         for vertex in range(self.N):
 
         # check if all vertices have a valid value
         for vertex in range(self.N):
@@ -188,20 +190,20 @@ class StrSaltHash(object):
     a random string of characters, summed up, and finally modulo NG is
     taken.
     """
     a random string of characters, summed up, and finally modulo NG is
     taken.
     """
+
     chars = string.ascii_letters + string.digits
 
     def __init__(self, N):
         self.N = N
     chars = string.ascii_letters + string.digits
 
     def __init__(self, N):
         self.N = N
-        self.salt = ''
+        self.salt = ""
 
     def __call__(self, key):
         # XXX: xkbcommon modification: make the salt length a power of 2
         #      so that the % operation in the hash is fast.
 
     def __call__(self, key):
         # XXX: xkbcommon modification: make the salt length a power of 2
         #      so that the % operation in the hash is fast.
-        while len(self.salt) < max(len(key), 32): # add more salt as necessary
+        while len(self.salt) < max(len(key), 32):  # add more salt as necessary
             self.salt += random.choice(self.chars)
 
             self.salt += random.choice(self.chars)
 
-        return sum(ord(self.salt[i]) * ord(c)
-                   for i, c in enumerate(key)) % self.N
+        return sum(ord(self.salt[i]) * ord(c) for i, c in enumerate(key)) % self.N
 
     template = """
 def hash_f(key, T):
 
     template = """
 def hash_f(key, T):
@@ -212,22 +214,23 @@ def perfect_hash(key):
             G[hash_f(key, "$S2")]) % $NG
 """
 
             G[hash_f(key, "$S2")]) % $NG
 """
 
+
 class IntSaltHash(object):
     """
     Random hash function generator.
     Simple byte level hashing, each byte is multiplied in sequence to a table
     containing random numbers, summed tp, and finally modulo NG is taken.
     """
 class IntSaltHash(object):
     """
     Random hash function generator.
     Simple byte level hashing, each byte is multiplied in sequence to a table
     containing random numbers, summed tp, and finally modulo NG is taken.
     """
+
     def __init__(self, N):
         self.N = N
         self.salt = []
 
     def __call__(self, key):
     def __init__(self, N):
         self.N = N
         self.salt = []
 
     def __call__(self, key):
-        while len(self.salt) < len(key): # add more salt as necessary
+        while len(self.salt) < len(key):  # add more salt as necessary
             self.salt.append(random.randint(1, self.N - 1))
 
             self.salt.append(random.randint(1, self.N - 1))
 
-        return sum(self.salt[i] * ord(c)
-                   for i, c in enumerate(key)) % self.N
+        return sum(self.salt[i] * ord(c) for i, c in enumerate(key)) % self.N
 
     template = """
 S1 = [$S1]
 
     template = """
 S1 = [$S1]
@@ -241,14 +244,18 @@ def perfect_hash(key):
     return (G[hash_f(key, S1)] + G[hash_f(key, S2)]) % $NG
 """
 
     return (G[hash_f(key, S1)] + G[hash_f(key, S2)]) % $NG
 """
 
+
 def builtin_template(Hash):
 def builtin_template(Hash):
-    return """\
+    return (
+        """\
 # =======================================================================
 # ================= Python code for perfect hash function ===============
 # =======================================================================
 
 G = [$G]
 # =======================================================================
 # ================= Python code for perfect hash function ===============
 # =======================================================================
 
 G = [$G]
-""" + Hash.template + """
+"""
+        + Hash.template
+        + """
 # ============================ Sanity check =============================
 
 K = [$K]
 # ============================ Sanity check =============================
 
 K = [$K]
@@ -257,6 +264,7 @@ assert len(K) == $NK
 for h, k in enumerate(K):
     assert perfect_hash(k) == h
 """
 for h, k in enumerate(K):
     assert perfect_hash(k) == h
 """
+    )
 
 
 class TooManyInterationsError(Exception):
 
 
 class TooManyInterationsError(Exception):
@@ -279,35 +287,38 @@ def generate_hash(keys, Hash=StrSaltHash):
         if not isinstance(key, str):
             raise TypeError("key a not string: %r" % key)
     if NK > 10000 and Hash == StrSaltHash:
         if not isinstance(key, str):
             raise TypeError("key a not string: %r" % key)
     if NK > 10000 and Hash == StrSaltHash:
-        print("""\
+        print(
+            """\
 WARNING: You have %d keys.
          Using --hft=1 is likely to fail for so many keys.
          Please use --hft=2 instead.
 WARNING: You have %d keys.
          Using --hft=1 is likely to fail for so many keys.
          Please use --hft=2 instead.
-""" % NK)
+"""
+            % NK
+        )
 
     # the number of vertices in the graph G
     NG = NK + 1
     if verbose:
 
     # the number of vertices in the graph G
     NG = NK + 1
     if verbose:
-        print('NG = %d' % NG)
+        print("NG = %d" % NG)
 
     trial = 0  # Number of trial graphs so far
     while True:
 
     trial = 0  # Number of trial graphs so far
     while True:
-        if (trial % trials) == 0:   # trials failures, increase NG slightly
+        if (trial % trials) == 0:  # trials failures, increase NG slightly
             if trial > 0:
                 NG = max(NG + 1, int(1.05 * NG))
             if verbose:
             if trial > 0:
                 NG = max(NG + 1, int(1.05 * NG))
             if verbose:
-                sys.stdout.write('\nGenerating graphs NG = %d ' % NG)
+                sys.stdout.write("\nGenerating graphs NG = %d " % NG)
         trial += 1
 
         if NG > 100 * (NK + 1):
             raise TooManyInterationsError("%d keys" % NK)
 
         if verbose:
         trial += 1
 
         if NG > 100 * (NK + 1):
             raise TooManyInterationsError("%d keys" % NK)
 
         if verbose:
-            sys.stdout.write('.')
+            sys.stdout.write(".")
             sys.stdout.flush()
 
             sys.stdout.flush()
 
-        G = Graph(NG)   # Create graph with NG vertices
-        f1 = Hash(NG)   # Create 2 random hash functions
+        G = Graph(NG)  # Create graph with NG vertices
+        f1 = Hash(NG)  # Create 2 random hash functions
         f2 = Hash(NG)
 
         # Connect vertices given by the values of the two hash functions
         f2 = Hash(NG)
 
         # Connect vertices given by the values of the two hash functions
@@ -322,33 +333,30 @@ WARNING: You have %d keys.
             break
 
     if verbose:
             break
 
     if verbose:
-        print('\nAcyclic graph found after %d trials.' % trial)
-        print('NG = %d' % NG)
+        print("\nAcyclic graph found after %d trials." % trial)
+        print("NG = %d" % NG)
 
     # Sanity check the result by actually verifying that all the keys
     # hash to the right value.
     for hashval, key in enumerate(keys):
 
     # Sanity check the result by actually verifying that all the keys
     # hash to the right value.
     for hashval, key in enumerate(keys):
-        assert hashval == (
-            G.vertex_values[f1(key)] + G.vertex_values[f2(key)]
-        ) % NG
+        assert hashval == (G.vertex_values[f1(key)] + G.vertex_values[f2(key)]) % NG
 
     if verbose:
 
     if verbose:
-        print('OK')
+        print("OK")
 
     return f1, f2, G.vertex_values
 
 
 class Format(object):
 
     return f1, f2, G.vertex_values
 
 
 class Format(object):
-
-    def __init__(self, width=76, indent=4, delimiter=', '):
+    def __init__(self, width=76, indent=4, delimiter=", "):
         self.width = width
         self.indent = indent
         self.delimiter = delimiter
 
     def print_format(self):
         print("Format options:")
         self.width = width
         self.indent = indent
         self.delimiter = delimiter
 
     def print_format(self):
         print("Format options:")
-        for name in 'width', 'indent', 'delimiter':
-            print('  %s: %r' % (name, getattr(self, name)))
+        for name in "width", "indent", "delimiter":
+            print("  %s: %r" % (name, getattr(self, name)))
 
     def __call__(self, data, quote=False):
         if not isinstance(data, (list, tuple)):
 
     def __call__(self, data, quote=False):
         if not isinstance(data, (list, tuple)):
@@ -360,10 +368,10 @@ class Format(object):
         for i, elt in enumerate(data):
             last = bool(i == len(data) - 1)
 
         for i, elt in enumerate(data):
             last = bool(i == len(data) - 1)
 
-            s = ('"%s"' if quote else '%s') % elt
+            s = ('"%s"' if quote else "%s") % elt
 
             if pos + len(s) + lendel > self.width:
 
             if pos + len(s) + lendel > self.width:
-                aux.write('\n' + (self.indent * ' '))
+                aux.write("\n" + (self.indent * " "))
                 pos = self.indent
 
             aux.write(s)
                 pos = self.indent
 
             aux.write(s)
@@ -372,7 +380,7 @@ class Format(object):
                 aux.write(self.delimiter)
                 pos += lendel
 
                 aux.write(self.delimiter)
                 pos += lendel
 
-        return '\n'.join(l.rstrip() for l in aux.getvalue().split('\n'))
+        return "\n".join(l.rstrip() for l in aux.getvalue().split("\n"))
 
 
 def generate_code(keys, Hash=StrSaltHash, template=None, options=None):
 
 
 def generate_code(keys, Hash=StrSaltHash, template=None, options=None):
@@ -397,20 +405,22 @@ def generate_code(keys, Hash=StrSaltHash, template=None, options=None):
     if options is None:
         fmt = Format()
     else:
     if options is None:
         fmt = Format()
     else:
-        fmt = Format(width=options.width, indent=options.indent,
-                     delimiter=options.delimiter)
+        fmt = Format(
+            width=options.width, indent=options.indent, delimiter=options.delimiter
+        )
 
     if verbose:
         fmt.print_format()
 
     return string.Template(template).substitute(
 
     if verbose:
         fmt.print_format()
 
     return string.Template(template).substitute(
-        NS = salt_len,
-        S1 = fmt(f1.salt),
-        S2 = fmt(f2.salt),
-        NG = len(G),
-        G  = fmt(G),
-        NK = len(keys),
-        K  = fmt(list(keys), quote=True))
+        NS=salt_len,
+        S1=fmt(f1.salt),
+        S2=fmt(f2.salt),
+        NG=len(G),
+        G=fmt(G),
+        NK=len(keys),
+        K=fmt(list(keys), quote=True),
+    )
 
 
 def read_table(filename, options):
 
 
 def read_table(filename, options):
@@ -430,15 +440,15 @@ def read_table(filename, options):
 
     if verbose:
         print("Reader options:")
 
     if verbose:
         print("Reader options:")
-        for name in 'comment', 'splitby', 'keycol':
-            print('  %s: %r' % (name, getattr(options, name)))
+        for name in "comment", "splitby", "keycol":
+            print("  %s: %r" % (name, getattr(options, name)))
 
     for n, line in enumerate(fi):
         line = line.strip()
         if not line or line.startswith(options.comment):
             continue
 
 
     for n, line in enumerate(fi):
         line = line.strip()
         if not line or line.startswith(options.comment):
             continue
 
-        if line.count(options.comment): # strip content after comment
+        if line.count(options.comment):  # strip content after comment
             line = line.split(options.comment)[0].strip()
 
         row = [col.strip() for col in line.split(options.splitby)]
             line = line.split(options.comment)[0].strip()
 
         row = [col.strip() for col in line.split(options.splitby)]
@@ -446,8 +456,9 @@ def read_table(filename, options):
         try:
             key = row[options.keycol - 1]
         except IndexError:
         try:
             key = row[options.keycol - 1]
         except IndexError:
-            sys.exit("%s:%d: Error: Cannot read key, not enough columns." %
-                     (filename, n + 1))
+            sys.exit(
+                "%s:%d: Error: Cannot read key, not enough columns." % (filename, n + 1)
+            )
 
         keys.append(key)
 
 
         keys.append(key)
 
@@ -463,7 +474,7 @@ def read_template(filename):
     if verbose:
         print("Reading template from file `%s'" % filename)
     try:
     if verbose:
         print("Reading template from file `%s'" % filename)
     try:
-        with open(filename, 'r') as fi:
+        with open(filename, "r") as fi:
             return fi.read()
     except IOError:
         sys.exit("Error: Could not open `%s' for reading." % filename)
             return fi.read()
     except IOError:
         sys.exit("Error: Could not open `%s' for reading." % filename)
@@ -471,8 +482,8 @@ def read_template(filename):
 
 def run_code(code):
     tmpdir = tempfile.mkdtemp()
 
 def run_code(code):
     tmpdir = tempfile.mkdtemp()
-    path = join(tmpdir, 't.py')
-    with open(path, 'w') as fo:
+    path = join(tmpdir, "t.py")
+    with open(path, "w") as fo:
         fo.write(code)
     try:
         subprocess.check_call([sys.executable, path])
         fo.write(code)
     try:
         subprocess.check_call([sys.executable, path])
@@ -494,102 +505,123 @@ If no template file is provided, a small built-in Python template
 is processed and the output code is written to stdout.
 """
 
 is processed and the output code is written to stdout.
 """
 
-    parser = OptionParser(usage = usage,
-                          description = description,
-                          prog = sys.argv[0],
-                          version = "%prog: " + __version__)
-
-    parser.add_option("--delimiter",
-                      action  = "store",
-                      default = ", ",
-                      help    = "Delimiter for list items used in output, "
-                                "the default delimiter is '%default'",
-                      metavar = "STR")
-
-    parser.add_option("--indent",
-                      action  = "store",
-                      default = 4,
-                      type    = "int",
-                      help    = "Make INT spaces at the beginning of a "
-                                "new line when generated list is wrapped. "
-                                "Default is %default",
-                      metavar = "INT")
-
-    parser.add_option("--width",
-                      action  = "store",
-                      default = 76,
-                      type    = "int",
-                      help    = "Maximal width of generated list when "
-                                "wrapped.  Default width is %default",
-                      metavar = "INT")
-
-    parser.add_option("--comment",
-                      action  = "store",
-                      default = "#",
-                      help    = "STR is the character, or sequence of "
-                                "characters, which marks the beginning "
-                                "of a comment (which runs till "
-                                "the end of the line), in the input "
-                                "KEYS_FILE. "
-                                "Default is '%default'",
-                      metavar = "STR")
-
-    parser.add_option("--splitby",
-                      action  = "store",
-                      default = ",",
-                      help    = "STR is the character by which the columns "
-                                "in the input KEYS_FILE are split. "
-                                "Default is '%default'",
-                      metavar = "STR")
-
-    parser.add_option("--keycol",
-                      action  = "store",
-                      default = 1,
-                      type    = "int",
-                      help    = "Specifies the column INT in the input "
-                                "KEYS_FILE which contains the keys. "
-                                "Default is %default, i.e. the first column.",
-                      metavar = "INT")
-
-    parser.add_option("--trials",
-                      action  = "store",
-                      default = 5,
-                      type    = "int",
-                      help    = "Specifies the number of trials before "
-                                "NG is increased.  A small INT will give "
-                                "compute faster, but the array G will be "
-                                "large.  A large INT will take longer to "
-                                "compute but G will be smaller. "
-                                "Default is %default",
-                      metavar = "INT")
-
-    parser.add_option("--hft",
-                      action  = "store",
-                      default = 1,
-                      type    = "int",
-                      help    = "Hash function type INT.  Possible values "
-                                "are 1 (StrSaltHash) and 2 (IntSaltHash). "
-                                "The default is %default",
-                      metavar = "INT")
-
-    parser.add_option("-e", "--execute",
-                      action  = "store_true",
-                      help    = "Execute the generated code within "
-                                "the Python interpreter.")
-
-    parser.add_option("-o", "--output",
-                      action  = "store",
-                      help    = "Specify output FILE explicitly. "
-                                "`-o std' means standard output. "
-                                "`-o no' means no output. "
-                                "By default, the file name is obtained "
-                                "from the name of the template file by "
-                                "substituting `tmpl' to `code'.",
-                      metavar = "FILE")
-
-    parser.add_option("-v", "--verbose",
-                      action = "store_true",
-                      help = "verbosity")
+    parser = OptionParser(
+        usage=usage,
+        description=description,
+        prog=sys.argv[0],
+        version="%prog: " + __version__,
+    )
+
+    parser.add_option(
+        "--delimiter",
+        action="store",
+        default=", ",
+        help="Delimiter for list items used in output, "
+        "the default delimiter is '%default'",
+        metavar="STR",
+    )
+
+    parser.add_option(
+        "--indent",
+        action="store",
+        default=4,
+        type="int",
+        help="Make INT spaces at the beginning of a "
+        "new line when generated list is wrapped. "
+        "Default is %default",
+        metavar="INT",
+    )
+
+    parser.add_option(
+        "--width",
+        action="store",
+        default=76,
+        type="int",
+        help="Maximal width of generated list when "
+        "wrapped.  Default width is %default",
+        metavar="INT",
+    )
+
+    parser.add_option(
+        "--comment",
+        action="store",
+        default="#",
+        help="STR is the character, or sequence of "
+        "characters, which marks the beginning "
+        "of a comment (which runs till "
+        "the end of the line), in the input "
+        "KEYS_FILE. "
+        "Default is '%default'",
+        metavar="STR",
+    )
+
+    parser.add_option(
+        "--splitby",
+        action="store",
+        default=",",
+        help="STR is the character by which the columns "
+        "in the input KEYS_FILE are split. "
+        "Default is '%default'",
+        metavar="STR",
+    )
+
+    parser.add_option(
+        "--keycol",
+        action="store",
+        default=1,
+        type="int",
+        help="Specifies the column INT in the input "
+        "KEYS_FILE which contains the keys. "
+        "Default is %default, i.e. the first column.",
+        metavar="INT",
+    )
+
+    parser.add_option(
+        "--trials",
+        action="store",
+        default=5,
+        type="int",
+        help="Specifies the number of trials before "
+        "NG is increased.  A small INT will give "
+        "compute faster, but the array G will be "
+        "large.  A large INT will take longer to "
+        "compute but G will be smaller. "
+        "Default is %default",
+        metavar="INT",
+    )
+
+    parser.add_option(
+        "--hft",
+        action="store",
+        default=1,
+        type="int",
+        help="Hash function type INT.  Possible values "
+        "are 1 (StrSaltHash) and 2 (IntSaltHash). "
+        "The default is %default",
+        metavar="INT",
+    )
+
+    parser.add_option(
+        "-e",
+        "--execute",
+        action="store_true",
+        help="Execute the generated code within " "the Python interpreter.",
+    )
+
+    parser.add_option(
+        "-o",
+        "--output",
+        action="store",
+        help="Specify output FILE explicitly. "
+        "`-o std' means standard output. "
+        "`-o no' means no output. "
+        "By default, the file name is obtained "
+        "from the name of the template file by "
+        "substituting `tmpl' to `code'.",
+        metavar="FILE",
+    )
+
+    parser.add_option("-v", "--verbose", action="store_true", help="verbosity")
 
     options, args = parser.parse_args()
 
 
     options, args = parser.parse_args()
 
@@ -605,7 +637,7 @@ is processed and the output code is written to stdout.
     if len(args) not in (1, 2):
         parser.error("incorrect number of arguments")
 
     if len(args) not in (1, 2):
         parser.error("incorrect number of arguments")
 
-    if len(args) == 2 and not args[1].count('tmpl'):
+    if len(args) == 2 and not args[1].count("tmpl"):
         parser.error("template filename does not contain 'tmpl'")
 
     if options.hft == 1:
         parser.error("template filename does not contain 'tmpl'")
 
     if options.hft == 1:
@@ -638,22 +670,22 @@ is processed and the output code is written to stdout.
         outname = options.output
     else:
         if tmpl_file:
         outname = options.output
     else:
         if tmpl_file:
-            if 'tmpl' not in tmpl_file:
+            if "tmpl" not in tmpl_file:
                 sys.exit("Hmm, template filename does not contain 'tmpl'")
                 sys.exit("Hmm, template filename does not contain 'tmpl'")
-            outname = tmpl_file.replace('tmpl', 'code')
+            outname = tmpl_file.replace("tmpl", "code")
         else:
         else:
-            outname = 'std'
+            outname = "std"
 
     if verbose:
         print("outname = %r\n" % outname)
 
 
     if verbose:
         print("outname = %r\n" % outname)
 
-    if outname == 'std':
+    if outname == "std":
         outstream = sys.stdout
         outstream = sys.stdout
-    elif outname == 'no':
+    elif outname == "no":
         outstream = None
     else:
         try:
         outstream = None
     else:
         try:
-            outstream = open(outname, 'w')
+            outstream = open(outname, "w")
         except IOError:
             sys.exit("Error: Could not open `%s' for writing." % outname)
 
         except IOError:
             sys.exit("Error: Could not open `%s' for writing." % outname)
 
@@ -661,14 +693,14 @@ is processed and the output code is written to stdout.
 
     if options.execute or template == builtin_template(Hash):
         if verbose:
 
     if options.execute or template == builtin_template(Hash):
         if verbose:
-            print('Executing code...\n')
+            print("Executing code...\n")
         run_code(code)
 
     if outstream:
         outstream.write(code)
         run_code(code)
 
     if outstream:
         outstream.write(code)
-        if not outname == 'std':
+        if not outname == "std":
             outstream.close()
 
 
             outstream.close()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()