Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

removed toolz, potential bugfixes, perfomance improvements, and more #43

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8e6e365
simplified `ordering` using a dict comprehension
jorenham Jan 14, 2024
751d7ec
simplified `supercedes`, cleared redundant if's
jorenham Jan 14, 2024
61fdc48
simplified `match`, prevent `_` shadowing
jorenham Jan 14, 2024
67d0b82
simplified dict comprehension in `VarDispatcher.__call__`
jorenham Jan 14, 2024
bdd5de4
replaced dict constructors by dict literals
jorenham Jan 14, 2024
73fc6ee
new-style class definition for `Dispatcher`
jorenham Jan 14, 2024
12efbf2
dict literal constructor
jorenham Jan 14, 2024
0b4d043
prevent cluttering the `dispatch` module namespace
jorenham Jan 14, 2024
0b87ded
fixed type comparison
jorenham Jan 14, 2024
1f35887
removed redundant `return`
jorenham Jan 14, 2024
fe9f2c0
improved toposort performance using `collections.deque`
jorenham Jan 14, 2024
bffa736
don't sort dicts in `freeze` with python>=3.7
jorenham Jan 14, 2024
4137875
detect loops in `transitive_get`
jorenham Jan 14, 2024
ff00223
simplified & fixed `assoc` for immutable mappings
jorenham Jan 14, 2024
c2e87a5
`stream_eval` cleanup
jorenham Jan 14, 2024
3430513
prevent returning `None` from `__instancecheck__`
jorenham Jan 14, 2024
31094f4
fixed type identity comparison
jorenham Jan 14, 2024
36c53a7
`__instancecheck__` performance improvements
jorenham Jan 14, 2024
9b82a9b
remove redundant `toolz` dependency
jorenham Jan 14, 2024
a091598
ran `black`
jorenham Jan 14, 2024
38bbc9c
fix failing `match.ordering` tests
jorenham Jan 14, 2024
7ecb533
fix failing `utils` tests
jorenham Jan 14, 2024
ef1cab7
`black`
jorenham Jan 14, 2024
afd4314
remove unused import
jorenham Jan 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 ""),
Expand Down
157 changes: 95 additions & 62 deletions unification/_version.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -59,28 +58,32 @@ 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
for command in commands:
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]
Expand Down Expand Up @@ -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")


Expand Down Expand Up @@ -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
Expand All @@ -191,32 +200,39 @@ 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:
print("likely tags: %s" % ",".join(sorted(tags)))
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")
Expand All @@ -233,20 +249,27 @@ 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)
raise NotThisMethod("'git rev-parse --git-dir' returned error")

# 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")
Expand All @@ -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")
Expand Down Expand Up @@ -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
Expand All @@ -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))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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():
Expand All @@ -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

Expand All @@ -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)
Expand All @@ -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,
}
20 changes: 8 additions & 12 deletions unification/core.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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

Expand All @@ -35,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


Expand Down
6 changes: 3 additions & 3 deletions unification/dispatch.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from functools import partial
from functools import partial as _partial

from multipledispatch import dispatch

namespace = dict()
namespace = {}

dispatch = partial(dispatch, namespace=namespace)
_partial = _partial(dispatch, namespace=namespace)
Loading