From 8e6e365dc4cab5cbdc6c855cb354419dfe47519c Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 08:30:18 +0100 Subject: [PATCH 01/24] simplified `ordering` using a dict comprehension --- unification/match.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/unification/match.py b/unification/match.py index 8833ee5..0bfc648 100644 --- a/unification/match.py +++ b/unification/match.py @@ -1,5 +1,3 @@ -from toolz import first, groupby - from .core import reify, unify from .utils import _toposort, freeze from .variable import isvar @@ -122,11 +120,7 @@ def ordering(signatures): Topological sort of edges as given by ``edge`` and ``supercedes`` """ - signatures = list(map(tuple, signatures)) - edges = [(a, b) for a in signatures for b in signatures if edge(a, b)] - edges = groupby(first, edges) - for s in signatures: - if s not in edges: - edges[s] = [] - edges = dict((k, [b for a, b in v]) for k, v in edges.items()) - return _toposort(edges) + return _toposort({ + s: [b for a, b in signatuples if edge(s, (a, b))] + for s in map(tuple, signatures) + }) From 751d7ec2f7cf7498f384c0951fb1598402176c11 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 08:36:10 +0100 Subject: [PATCH 02/24] simplified `supercedes`, cleared redundant if's --- unification/match.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/unification/match.py b/unification/match.py index 0bfc648..6450495 100644 --- a/unification/match.py +++ b/unification/match.py @@ -95,11 +95,8 @@ def supercedes(a, b): s = unify(a, b) if s is False: return False - s = dict((k, v) for k, v in s.items() if not isvar(k) or not isvar(v)) - if reify(a, s) == a: - return True - if reify(b, s) == b: - return False + s = {k: v for k, v in s.items() if not isvar(k) or not isvar(v)} + return reify(a, s) == a def edge(a, b, tie_breaker=hash): From 61fdc488a1f4443b5f7199ea828c00903918f84c Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 08:43:10 +0100 Subject: [PATCH 03/24] simplified `match`, prevent `_` shadowing --- unification/match.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/unification/match.py b/unification/match.py index 6450495..edacc76 100644 --- a/unification/match.py +++ b/unification/match.py @@ -74,18 +74,14 @@ def match(*signature, **kwargs): namespace = kwargs.get("namespace", global_namespace) dispatcher = kwargs.get("Dispatcher", Dispatcher) - def _(func): + def _match(func): name = func.__name__ - if name not in namespace: - namespace[name] = dispatcher(name) - d = namespace[name] - + d = namespace.setdefault(name, dispatcher(name)) d.add(signature, func) - return d - return _ + return _match def supercedes(a, b): From 67d0b828f1e9554bdd58a6c1794343c294c8684d Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 08:46:02 +0100 Subject: [PATCH 04/24] simplified dict comprehension in `VarDispatcher.__call__` --- unification/match.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unification/match.py b/unification/match.py index edacc76..e1ab0bb 100644 --- a/unification/match.py +++ b/unification/match.py @@ -63,7 +63,7 @@ class VarDispatcher(Dispatcher): def __call__(self, *args, **kwargs): func, s = self.resolve(args) - d = dict((k.token, v) for k, v in s.items()) + d = {k.token: v for k, v in s.items()} return func(**d) From bdd5de4e19c90fc7df028f878ea4217c4b7c7790 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 08:47:14 +0100 Subject: [PATCH 05/24] replaced dict constructors by dict literals --- unification/match.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unification/match.py b/unification/match.py index e1ab0bb..e3fdcab 100644 --- a/unification/match.py +++ b/unification/match.py @@ -6,7 +6,7 @@ class Dispatcher(object): def __init__(self, name): self.name = name - self.funcs = dict() + self.funcs = {} self.ordering = [] def add(self, signature, func): @@ -67,7 +67,7 @@ def __call__(self, *args, **kwargs): return func(**d) -global_namespace = dict() +global_namespace = {} def match(*signature, **kwargs): From 73fc6ee772a6e9e6924c84431f457931411bb453 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 09:10:18 +0100 Subject: [PATCH 06/24] new-style class definition for `Dispatcher` --- unification/match.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unification/match.py b/unification/match.py index e3fdcab..6c17124 100644 --- a/unification/match.py +++ b/unification/match.py @@ -3,7 +3,7 @@ from .variable import isvar -class Dispatcher(object): +class Dispatcher: def __init__(self, name): self.name = name self.funcs = {} From 12efbf22fe15d449c4c37611479a6cb7932a551b Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 09:14:12 +0100 Subject: [PATCH 07/24] dict literal constructor --- unification/dispatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unification/dispatch.py b/unification/dispatch.py index 4a5606e..d3b20b9 100644 --- a/unification/dispatch.py +++ b/unification/dispatch.py @@ -2,6 +2,6 @@ from multipledispatch import dispatch -namespace = dict() +namespace = {} dispatch = partial(dispatch, namespace=namespace) From 0b4d0435c0675244f3b303f439b2c72fe055f0e9 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 09:15:53 +0100 Subject: [PATCH 08/24] prevent cluttering the `dispatch` module namespace --- unification/dispatch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unification/dispatch.py b/unification/dispatch.py index d3b20b9..b815c15 100644 --- a/unification/dispatch.py +++ b/unification/dispatch.py @@ -1,7 +1,7 @@ -from functools import partial +from functools import partial as _partial from multipledispatch import dispatch namespace = {} -dispatch = partial(dispatch, namespace=namespace) +_partial = _partial(dispatch, namespace=namespace) From 0b87dedba039571b99024629b5a885780833d812 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 09:19:55 +0100 Subject: [PATCH 09/24] fixed type comparison --- unification/more.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unification/more.py b/unification/more.py index 071e638..3b5c71c 100644 --- a/unification/more.py +++ b/unification/more.py @@ -100,7 +100,7 @@ def _unify_object(u, v, s): >>> unify_object(f, g, {}) {~x: 2} """ - if type(u) != type(v): + if type(u) is not type(v): yield False return From 1f358874397a1d4ce99ae2cf2710c3221b054ebb Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 09:21:38 +0100 Subject: [PATCH 10/24] removed redundant `return` --- unification/more.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/unification/more.py b/unification/more.py index 3b5c71c..1872bd0 100644 --- a/unification/more.py +++ b/unification/more.py @@ -102,9 +102,7 @@ def _unify_object(u, v, s): """ if type(u) is not type(v): yield False - return - - if hasattr(u, "__slots__"): + elif hasattr(u, "__slots__"): yield _unify( tuple(getattr(u, slot) for slot in u.__slots__), tuple(getattr(v, slot) for slot in v.__slots__), From fe9f2c08f6a1669f529d6492c7b5750258bb246a Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 09:43:52 +0100 Subject: [PATCH 11/24] improved toposort performance using `collections.deque` --- unification/utils.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/unification/utils.py b/unification/utils.py index 34735f4..5595716 100644 --- a/unification/utils.py +++ b/unification/utils.py @@ -1,3 +1,4 @@ +from collections import deque from collections.abc import Mapping, Set from contextlib import suppress @@ -36,21 +37,23 @@ def _toposort(edges): Communications of the ACM [2] http://en.wikipedia.org/wiki/Toposort#Algorithms """ - incoming_edges = reverse_dict(edges) - incoming_edges = dict((k, set(val)) for k, val in incoming_edges.items()) - S = set((v for v in edges if v not in incoming_edges)) + incoming_edges = {k: set(val) for k, val in reverse_dict(edges).items()} + + S = deque(v for v in edges if v not in incoming_edges) L = [] while S: - n = S.pop() - L.append(n) + n = S.popleft() for m in edges.get(n, ()): - assert n in incoming_edges[m] - incoming_edges[m].remove(n) - if not incoming_edges[m]: - S.add(m) - if any(incoming_edges.get(v, None) for v in edges): + edges_m = incoming_edges[m] + edges_m.remove(n) + if not edges_m: + S.append(m) + L.append(n) + + if any(incoming_edges.get(v) for v in edges): raise ValueError("Input has cycles") + return L @@ -70,7 +73,7 @@ def reverse_dict(d): result = {} for key in d: for val in d[key]: - result[val] = result.get(val, tuple()) + (key,) + result[val] = result.get(val, ()) + (key,) return result From bffa736d2570cf35cd856753c035628a938fc585 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 10:26:50 +0100 Subject: [PATCH 12/24] don't sort dicts in `freeze` with python>=3.7 --- unification/utils.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/unification/utils.py b/unification/utils.py index 5595716..c97b302 100644 --- a/unification/utils.py +++ b/unification/utils.py @@ -1,7 +1,10 @@ +import sys from collections import deque -from collections.abc import Mapping, Set +from collections.abc import Mapping, Sequence, Set from contextlib import suppress +__PY37 = sys.version_info >= (3, 7) + def transitive_get(key, d): """Get a value for a dict key in a transitive fashion. @@ -90,9 +93,13 @@ def freeze(d): ((1, 2),) """ if isinstance(d, Mapping): - return tuple(map(freeze, sorted(d.items(), key=lambda x: hash(x[0])))) + if __PY37: + items = d.items() + else: + items = sorted(d.items(), key=lambda x: hash(x[0])) + return tuple(map(freeze, items)) if isinstance(d, Set): return tuple(map(freeze, sorted(d, key=hash))) - if isinstance(d, (tuple, list)): + if isinstance(d, Sequence): return tuple(map(freeze, d)) return d From 41378758dbe5b3a24d0ef880f9e0f2b110b6d2dc Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 10:48:03 +0100 Subject: [PATCH 13/24] detect loops in `transitive_get` --- unification/utils.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/unification/utils.py b/unification/utils.py index c97b302..3c73015 100644 --- a/unification/utils.py +++ b/unification/utils.py @@ -15,9 +15,16 @@ def transitive_get(key, d): >>> transitive_get(1, d) 4 """ - with suppress(TypeError): - while key in d: - key = d[key] + for _ in range(len(d)): + key = d[key] + + with suppress(TypeError): + if key in d: + continue + break + else: + raise RecursionError('dict contains a loop') + return key From ff002231166fe68489e5645d37b83ba5148a78e4 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 11:02:29 +0100 Subject: [PATCH 14/24] simplified & fixed `assoc` for immutable mappings --- unification/core.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/unification/core.py b/unification/core.py index cfd827b..ed27856 100644 --- a/unification/core.py +++ b/unification/core.py @@ -1,6 +1,5 @@ from collections import OrderedDict, deque from collections.abc import Generator, Iterator, Mapping, Set -from copy import copy from functools import partial from operator import length_hint @@ -16,10 +15,7 @@ @dispatch(Mapping, object, object) def assoc(s, u, v): """Add an entry to a `Mapping` and return it.""" - if hasattr(s, "copy"): - s = s.copy() - else: - s = copy(s) # pragma: no cover + s = dict(s) s[u] = v return s From c2e87a5359043797578a90e353b4823107ceec20 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 11:07:23 +0100 Subject: [PATCH 15/24] `stream_eval` cleanup --- unification/core.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/unification/core.py b/unification/core.py index ed27856..ef31f17 100644 --- a/unification/core.py +++ b/unification/core.py @@ -31,27 +31,27 @@ def stream_eval(z, res_filter=None): if not isinstance(z, Generator): return z - stack = deque() + stack = deque([z]) z_args, z_out = None, None - stack.append(z) while stack: z = stack[-1] try: z_out = z.send(z_args) - if res_filter: - _ = res_filter(z, z_out) + if res_filter is not None: + res_filter(z, z_out) + except StopIteration: + stack.pop() + + else: if isinstance(z_out, Generator): stack.append(z_out) z_args = None else: z_args = z_out - except StopIteration: - _ = stack.pop() - return z_out From 3430513fbeb2ef70edd7fadb3d3fa8f05e0de8c8 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 11:10:23 +0100 Subject: [PATCH 16/24] prevent returning `None` from `__instancecheck__` --- unification/variable.py | 1 + 1 file changed, 1 insertion(+) diff --git a/unification/variable.py b/unification/variable.py index c7b3710..504f96c 100644 --- a/unification/variable.py +++ b/unification/variable.py @@ -10,6 +10,7 @@ class LVarType(ABCMeta): def __instancecheck__(self, o): with suppress(TypeError): return issubclass(type(o), (Var, LVarType)) or o in _glv + return False class Var(metaclass=LVarType): From 31094f4ed42da7d24985a7c0256e7b9a23be818e Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 11:17:38 +0100 Subject: [PATCH 17/24] fixed type identity comparison --- unification/variable.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/unification/variable.py b/unification/variable.py index 504f96c..728ec94 100644 --- a/unification/variable.py +++ b/unification/variable.py @@ -2,8 +2,7 @@ from abc import ABCMeta from contextlib import contextmanager, suppress -_global_logic_variables = set() -_glv = _global_logic_variables +_glv = _global_logic_variables = set() class LVarType(ABCMeta): @@ -45,7 +44,7 @@ def __new__(cls, token=None, prefix=""): output. """ if token is None: - token = f"{prefix}_{Var._id}" + token = f"{prefix}_{cls._id}" cls._id += 1 obj = cls._refs.get(token, None) @@ -63,7 +62,7 @@ def __str__(self): __repr__ = __str__ def __eq__(self, other): - if type(self) == type(other): + if type(self) is type(other): return self.token == other.token return NotImplemented From 36c53a78feef865f2d352af74a37fd5bda5e2fef Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 11:30:29 +0100 Subject: [PATCH 18/24] `__instancecheck__` performance improvements --- unification/variable.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/unification/variable.py b/unification/variable.py index 728ec94..6d3ad43 100644 --- a/unification/variable.py +++ b/unification/variable.py @@ -1,15 +1,19 @@ import weakref from abc import ABCMeta -from contextlib import contextmanager, suppress +from contextlib import contextmanager _glv = _global_logic_variables = set() class LVarType(ABCMeta): def __instancecheck__(self, o): - with suppress(TypeError): - return issubclass(type(o), (Var, LVarType)) or o in _glv - return False + if issubclass(type(o), Var): + return True + + try: + return o in _glv + except TypeError: + return False class Var(metaclass=LVarType): From 9b82a9b07ab9f290fa39c1d41c9f41262cd5cb02 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 14 Jan 2024 11:31:22 +0100 Subject: [PATCH 19/24] remove redundant `toolz` dependency --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 5d6b734..c43a737 100755 --- a/setup.py +++ b/setup.py @@ -17,7 +17,6 @@ keywords="unification logic-programming dispatch", packages=["unification"], install_requires=[ - "toolz", "multipledispatch", ], long_description=(open("README.md").read() if exists("README.md") else ""), From a0915982f2c13e7f55b78e2ba37f811a5f28235f Mon Sep 17 00:00:00 2001 From: jorenham Date: Sun, 14 Jan 2024 12:06:14 +0100 Subject: [PATCH 20/24] ran `black` --- unification/_version.py | 157 ++++++++++++++++++++++++---------------- unification/match.py | 10 ++- unification/utils.py | 6 +- 3 files changed, 104 insertions(+), 69 deletions(-) diff --git a/unification/_version.py b/unification/_version.py index bff98d1..e103428 100644 --- a/unification/_version.py +++ b/unification/_version.py @@ -1,4 +1,3 @@ - # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -59,17 +58,18 @@ class NotThisMethod(Exception): def register_vcs_handler(vcs, method): # decorator """Create decorator to mark a method as the handler of a VCS.""" + def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f + return decorate -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, - env=None): +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): """Call the given command(s).""" assert isinstance(commands, list) process = None @@ -77,10 +77,13 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, try: dispcmd = str([command] + args) # remember shell=False, so use git.cmd on windows, not just git - process = subprocess.Popen([command] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) + process = subprocess.Popen( + [command] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None), + ) break except OSError: e = sys.exc_info()[1] @@ -115,15 +118,21 @@ def versions_from_parentdir(parentdir_prefix, root, verbose): for _ in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} + return { + "version": dirname[len(parentdir_prefix) :], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None, + } rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: - print("Tried directories %s but none started with prefix %s" % - (str(rootdirs), parentdir_prefix)) + print( + "Tried directories %s but none started with prefix %s" + % (str(rootdirs), parentdir_prefix) + ) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @@ -182,7 +191,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " - tags = {r[len(TAG):] for r in refs if r.startswith(TAG)} + tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)} if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d @@ -191,7 +200,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". - tags = {r for r in refs if re.search(r'\d', r)} + tags = {r for r in refs if re.search(r"\d", r)} if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: @@ -199,24 +208,31 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] + r = ref[len(tag_prefix) :] # Filter out refs that exactly match prefix or that don't start # with a number once the prefix is stripped (mostly a concern # when prefix is '') - if not re.match(r'\d', r): + if not re.match(r"\d", r): continue if verbose: print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date, + } # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None, + } @register_vcs_handler("git", "pieces_from_vcs") @@ -233,8 +249,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): GITS = ["git.cmd", "git.exe"] TAG_PREFIX_REGEX = r"\*" - _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=True) + _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) if rc != 0: if verbose: print("Directory %s not under git control" % root) @@ -242,11 +257,19 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty", - "--always", "--long", - "--match", - "%s%s" % (tag_prefix, TAG_PREFIX_REGEX)], - cwd=root) + describe_out, rc = runner( + GITS, + [ + "describe", + "--tags", + "--dirty", + "--always", + "--long", + "--match", + "%s%s" % (tag_prefix, TAG_PREFIX_REGEX), + ], + cwd=root, + ) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") @@ -261,8 +284,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): pieces["short"] = full_out[:7] # maybe improved later pieces["error"] = None - branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], - cwd=root) + branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root) # --abbrev-ref was added in git-1.6.3 if rc != 0 or branch_name is None: raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") @@ -302,17 +324,16 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] + git_describe = git_describe[: git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) if not mo: # unparsable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) + pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out return pieces # tag @@ -321,10 +342,12 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) + pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( + full_tag, + tag_prefix, + ) return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] + pieces["closest-tag"] = full_tag[len(tag_prefix) :] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) @@ -373,8 +396,7 @@ def render_pep440(pieces): rendered += ".dirty" else: # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -403,8 +425,7 @@ def render_pep440_branch(pieces): rendered = "0" if pieces["branch"] != "master": rendered += ".dev0" - rendered += "+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -432,7 +453,7 @@ def render_pep440_pre(pieces): tag_version, post_version = pep440_split_post(pieces["closest-tag"]) rendered = tag_version if post_version is not None: - rendered += ".post%d.dev%d" % (post_version+1, pieces["distance"]) + rendered += ".post%d.dev%d" % (post_version + 1, pieces["distance"]) else: rendered += ".post0.dev%d" % (pieces["distance"]) else: @@ -565,11 +586,13 @@ def render_git_describe_long(pieces): def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None, + } if not style or style == "default": style = "pep440" # the default @@ -593,9 +616,13 @@ def render(pieces, style): else: raise ValueError("unknown style '%s'" % style) - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date"), + } def get_versions(): @@ -609,8 +636,7 @@ def get_versions(): verbose = cfg.verbose try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) except NotThisMethod: pass @@ -619,13 +645,16 @@ def get_versions(): # versionfile_source is the relative path from the top of the source # tree (where the .git directory might live) to this file. Invert # this to find the root from __file__. - for _ in cfg.versionfile_source.split('/'): + for _ in cfg.versionfile_source.split("/"): root = os.path.dirname(root) except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None, + } try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) @@ -639,6 +668,10 @@ def get_versions(): except NotThisMethod: pass - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None, + } diff --git a/unification/match.py b/unification/match.py index 6c17124..8248d4e 100644 --- a/unification/match.py +++ b/unification/match.py @@ -113,7 +113,9 @@ def ordering(signatures): Topological sort of edges as given by ``edge`` and ``supercedes`` """ - return _toposort({ - s: [b for a, b in signatuples if edge(s, (a, b))] - for s in map(tuple, signatures) - }) + return _toposort( + { + s: [b for a, b in signatuples if edge(s, (a, b))] + for s in map(tuple, signatures) + } + ) diff --git a/unification/utils.py b/unification/utils.py index 3c73015..948c099 100644 --- a/unification/utils.py +++ b/unification/utils.py @@ -23,7 +23,7 @@ def transitive_get(key, d): continue break else: - raise RecursionError('dict contains a loop') + raise RecursionError("dict contains a loop") return key @@ -60,10 +60,10 @@ def _toposort(edges): if not edges_m: S.append(m) L.append(n) - + if any(incoming_edges.get(v) for v in edges): raise ValueError("Input has cycles") - + return L From 38bbc9ccbedbf6bda50419e29831e7d68b9529d4 Mon Sep 17 00:00:00 2001 From: jorenham Date: Sun, 14 Jan 2024 12:19:40 +0100 Subject: [PATCH 21/24] fix failing `match.ordering` tests --- unification/match.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unification/match.py b/unification/match.py index 8248d4e..d7e0784 100644 --- a/unification/match.py +++ b/unification/match.py @@ -115,7 +115,7 @@ def ordering(signatures): """ return _toposort( { - s: [b for a, b in signatuples if edge(s, (a, b))] + s: [t for t in signatures if edge(s, t)] for s in map(tuple, signatures) } ) From 7ecb53315c349b27b599d235e8f5276e56a414b5 Mon Sep 17 00:00:00 2001 From: jorenham Date: Sun, 14 Jan 2024 12:21:46 +0100 Subject: [PATCH 22/24] fix failing `utils` tests --- unification/utils.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/unification/utils.py b/unification/utils.py index 948c099..758d51f 100644 --- a/unification/utils.py +++ b/unification/utils.py @@ -15,13 +15,13 @@ def transitive_get(key, d): >>> transitive_get(1, d) 4 """ - for _ in range(len(d)): - key = d[key] - - with suppress(TypeError): - if key in d: - continue - break + for _ in range(len(d) + 1): + try: + if key not in d: + break + key = d[key] + except TypeError: + break else: raise RecursionError("dict contains a loop") @@ -99,6 +99,8 @@ def freeze(d): >>> freeze({1: 2}) # doctest: +SKIP ((1, 2),) """ + if isinstance(d, (str, bytes)): + return d if isinstance(d, Mapping): if __PY37: items = d.items() From ef1cab78b55723efe92f82d862290fa4ebcece76 Mon Sep 17 00:00:00 2001 From: jorenham Date: Sun, 14 Jan 2024 12:22:12 +0100 Subject: [PATCH 23/24] `black` --- unification/match.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/unification/match.py b/unification/match.py index d7e0784..f6722d5 100644 --- a/unification/match.py +++ b/unification/match.py @@ -114,8 +114,5 @@ def ordering(signatures): Topological sort of edges as given by ``edge`` and ``supercedes`` """ return _toposort( - { - s: [t for t in signatures if edge(s, t)] - for s in map(tuple, signatures) - } + {s: [t for t in signatures if edge(s, t)] for s in map(tuple, signatures)} ) From afd4314a8ea2d06fd3bc2f4000057100ebafffe2 Mon Sep 17 00:00:00 2001 From: jorenham Date: Sun, 14 Jan 2024 12:23:33 +0100 Subject: [PATCH 24/24] remove unused import --- unification/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/unification/utils.py b/unification/utils.py index 758d51f..91ae974 100644 --- a/unification/utils.py +++ b/unification/utils.py @@ -1,7 +1,6 @@ import sys from collections import deque from collections.abc import Mapping, Sequence, Set -from contextlib import suppress __PY37 = sys.version_info >= (3, 7)