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

Drop Python 3.9, add 3.12; update pre-commit #84

Merged
merged 3 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
coverage:
status:
project:
default:
informational: true
patch:
default:
informational: true
changes: false
comment:
layout: "header, diff"
behavior: default
github_checks:
annotations: false
2 changes: 1 addition & 1 deletion .github/workflows/publish_pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
python-version: "3.10"
- name: Install build dependencies
run: |
python -m pip install --upgrade pip
Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
fail-fast: true
matrix:
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down Expand Up @@ -49,9 +49,10 @@ jobs:
- name: Install dependencies
run: |
$(command -v mamba || command -v conda) install python-suitesparse-graphblas scipy pandas donfig pyyaml numpy python-graphblas \
pytest-cov pytest-randomly pytest-mpl
pytest-cov pytest-randomly pytest-mpl networkx
# matplotlib lxml pygraphviz pydot sympy # Extra networkx deps we don't need yet
pip install git+https://github.com/networkx/networkx.git@main --no-deps
# Sometimes we prefer to use the latest release of NetworkX or the latest development from github
# pip install git+https://github.com/networkx/networkx.git@main --no-deps
pip install -e . --no-deps
- name: PyTest
run: |
Expand Down
12 changes: 6 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,26 @@ repos:
- id: autoflake
args: [--in-place]
- repo: https://github.com/pycqa/isort
rev: 5.12.0
rev: 5.13.1
hooks:
- id: isort
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.0
hooks:
- id: pyupgrade
args: [--py39-plus]
args: [--py310-plus]
- repo: https://github.com/MarcoGorelli/auto-walrus
rev: v0.2.2
hooks:
- id: auto-walrus
args: [--line-length, "100"]
- repo: https://github.com/psf/black
rev: 23.10.0
rev: 23.12.0
hooks:
- id: black
# - id: black-jupyter
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.0
rev: v0.1.7
hooks:
- id: ruff
args: [--fix-only, --show-fixes]
Expand All @@ -74,7 +74,7 @@ repos:
additional_dependencies: &flake8_dependencies
# These versions need updated manually
- flake8==6.1.0
- flake8-bugbear==23.9.16
- flake8-bugbear==23.12.2
- flake8-simplify==0.21.0
- repo: https://github.com/asottile/yesqa
rev: v1.5.0
Expand All @@ -89,7 +89,7 @@ repos:
additional_dependencies: [tomli]
files: ^(graphblas_algorithms|docs)/
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.0
rev: v0.1.7
hooks:
- id: ruff
# `pyroma` may help keep our package standards up to date if best practices change.
Expand Down
4 changes: 2 additions & 2 deletions graphblas_algorithms/algorithms/operators/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def intersection(G, H, *, name="intersection"):
ids = H.list_to_ids(keys)
B = H._A[ids, ids].new(dtypes.unify(A.dtype, H._A.dtype), mask=A.S, name=name)
B << unary.one(B)
return type(G)(B, key_to_id=dict(zip(keys, range(len(keys)))))
return type(G)(B, key_to_id=dict(zip(keys, range(len(keys)), strict=True)))


def difference(G, H, *, name="difference"):
Expand Down Expand Up @@ -142,7 +142,7 @@ def compose(G, H, *, name="compose"):
C[A.nrows :, A.ncols :] = B[ids, ids]
# Now make new `key_to_id`
ids += A.nrows
key_to_id = dict(zip(newkeys, ids.tolist()))
key_to_id = dict(zip(newkeys, ids.tolist(), strict=True))
key_to_id.update(G._key_to_id)
return type(G)(C, key_to_id=key_to_id)

Expand Down
2 changes: 1 addition & 1 deletion graphblas_algorithms/algorithms/shortest_paths/weighted.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def bellman_ford_path_lengths(G, nodes=None, *, expand_output=False):

def _reconstruct_path_from_parents(G, parents, src, dst):
indices, values = parents.to_coo(sort=False)
d = dict(zip(indices.tolist(), values.tolist()))
d = dict(zip(indices.tolist(), values.tolist(), strict=True))
if dst not in d:
return []
cur = dst
Expand Down
23 changes: 13 additions & 10 deletions graphblas_algorithms/classes/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def dict_to_vector(self, d, *, size=None, dtype=None, name=None):
if size is None:
size = len(self)
key_to_id = self._key_to_id
indices, values = zip(*((key_to_id[key], val) for key, val in d.items()))
indices, values = zip(*((key_to_id[key], val) for key, val in d.items()), strict=True)
return Vector.from_coo(indices, values, size=size, dtype=dtype, name=name)


Expand Down Expand Up @@ -116,7 +116,7 @@ def vector_to_dict(self, v, *, mask=None, fill_value=None):
elif fill_value is not None and v.nvals < v.size:
v(mask=~v.S) << fill_value
id_to_key = self.id_to_key
return {id_to_key[index]: value for index, value in zip(*v.to_coo(sort=False))}
return {id_to_key[index]: value for index, value in zip(*v.to_coo(sort=False), strict=True)}


def vector_to_list(self, v, *, values_are_keys=False):
Expand Down Expand Up @@ -198,26 +198,29 @@ def matrix_to_dicts(self, A, *, use_row_index=False, use_column_index=False, val
id_to_key = self.id_to_key
if values_are_keys:
values = [id_to_key[val] for val in values]
it = zip(rows, np.lib.stride_tricks.sliding_window_view(indptr, 2).tolist())
it = zip(rows, np.lib.stride_tricks.sliding_window_view(indptr, 2).tolist(), strict=True)
if use_row_index and use_column_index:
return {
row: dict(zip(col_indices[start:stop], values[start:stop])) for row, (start, stop) in it
row: dict(zip(col_indices[start:stop], values[start:stop], strict=True))
for row, (start, stop) in it
}
if use_row_index:
return {
row: {
id_to_key[col]: val for col, val in zip(col_indices[start:stop], values[start:stop])
id_to_key[col]: val
for col, val in zip(col_indices[start:stop], values[start:stop], strict=True)
}
for row, (start, stop) in it
}
if use_column_index:
return {
id_to_key[row]: dict(zip(col_indices[start:stop], values[start:stop]))
id_to_key[row]: dict(zip(col_indices[start:stop], values[start:stop], strict=True))
for row, (start, stop) in it
}
return {
id_to_key[row]: {
id_to_key[col]: val for col, val in zip(col_indices[start:stop], values[start:stop])
id_to_key[col]: val
for col, val in zip(col_indices[start:stop], values[start:stop], strict=True)
}
for row, (start, stop) in it
}
Expand All @@ -239,9 +242,9 @@ def to_networkx(self, edge_attribute="weight"):
rows = (id_to_key[row] for row in rows.tolist())
cols = (id_to_key[col] for col in cols.tolist())
if edge_attribute is None:
G.add_edges_from(zip(rows, cols))
G.add_edges_from(zip(rows, cols, strict=True))
else:
G.add_weighted_edges_from(zip(rows, cols, vals), weight=edge_attribute)
G.add_weighted_edges_from(zip(rows, cols, vals, strict=True), weight=edge_attribute)
# What else should we copy over?
return G

Expand All @@ -258,4 +261,4 @@ def renumber_key_to_id(self, indices):
return {id_to_key[index]: i for i, index in enumerate(indices)}
# Alternative (about the same performance)
# keys = self.list_to_keys(indices)
# return dict(zip(keys, range(len(indices))))
# return dict(zip(keys, range(len(indices)), strict=True))
32 changes: 16 additions & 16 deletions graphblas_algorithms/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,31 +281,31 @@ def key(testpath):
return (testname, frozenset({filename}))

# Reasons to skip tests
multi_attributed = "unable to handle multi-attributed graphs"
# multi_attributed = "unable to handle multi-attributed graphs"
multidigraph = "unable to handle MultiDiGraph"
multigraph = "unable to handle MultiGraph"

# Which tests to skip
skip = {
key("test_mst.py:TestBoruvka.test_attributes"): multi_attributed,
key("test_mst.py:TestBoruvka.test_weight_attribute"): multi_attributed,
# key("test_mst.py:TestBoruvka.test_attributes"): multi_attributed,
# key("test_mst.py:TestBoruvka.test_weight_attribute"): multi_attributed,
key("test_dense.py:TestFloyd.test_zero_weight"): multidigraph,
key("test_dense_numpy.py:test_zero_weight"): multidigraph,
key("test_weighted.py:TestBellmanFordAndGoldbergRadzik.test_multigraph"): multigraph,
key("test_binary.py:test_compose_multigraph"): multigraph,
key("test_binary.py:test_difference_multigraph_attributes"): multigraph,
key("test_binary.py:test_disjoint_union_multigraph"): multigraph,
key("test_binary.py:test_full_join_multigraph"): multigraph,
key("test_binary.py:test_intersection_multigraph_attributes"): multigraph,
key(
"test_binary.py:test_intersection_multigraph_attributes_node_set_different"
): multigraph,
key("test_binary.py:test_symmetric_difference_multigraph"): multigraph,
key("test_binary.py:test_union_attributes"): multi_attributed,
# key("test_binary.py:test_compose_multigraph"): multigraph,
# key("test_binary.py:test_difference_multigraph_attributes"): multigraph,
# key("test_binary.py:test_disjoint_union_multigraph"): multigraph,
# key("test_binary.py:test_full_join_multigraph"): multigraph,
# key("test_binary.py:test_intersection_multigraph_attributes"): multigraph,
# key(
# "test_binary.py:test_intersection_multigraph_attributes_node_set_different"
# ): multigraph,
# key("test_binary.py:test_symmetric_difference_multigraph"): multigraph,
# key("test_binary.py:test_union_attributes"): multi_attributed,
Comment on lines +295 to +304
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before I delete these, I want to verify that we expect these tests to pass. I probably made a change earlier.

# TODO: move failing assertion from `test_union_and_compose`
key("test_binary.py:test_union_and_compose"): multi_attributed,
key("test_binary.py:test_union_multigraph"): multigraph,
key("test_vf2pp.py:test_custom_multigraph4_different_labels"): multigraph,
# key("test_binary.py:test_union_and_compose"): multi_attributed,
# key("test_binary.py:test_union_multigraph"): multigraph,
# key("test_vf2pp.py:test_custom_multigraph4_different_labels"): multigraph,
}
for item in items:
kset = set(item.keywords)
Expand Down
6 changes: 5 additions & 1 deletion graphblas_algorithms/nxapi/boundary.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,19 @@ def edge_boundary(G, nbunch1, nbunch2=None, data=False, keys=False, default=None
(id_to_key[col] for col in cols),
# Unsure about this; data argument may mean *all* edge attributes
({weight: val} for val in vals),
strict=True,
)
else:
it = zip(
(id_to_key[row] for row in rows),
(id_to_key[col] for col in cols),
strict=True,
)
if is_multigraph:
# Edge weights indicate number of times to repeat edges
it = itertools.chain.from_iterable(itertools.starmap(itertools.repeat, zip(it, vals)))
it = itertools.chain.from_iterable(
itertools.starmap(itertools.repeat, zip(it, vals, strict=True))
)
return it


Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ name = "graphblas-algorithms"
dynamic = ["version"]
description = "Graph algorithms written in GraphBLAS and backend for NetworkX"
readme = "README.md"
requires-python = ">=3.9"
requires-python = ">=3.10"
license = {file = "LICENSE"}
authors = [
{name = "Erik Welch", email = "erik.n.welch@gmail.com"},
Expand Down Expand Up @@ -43,7 +43,6 @@ classifiers = [
"Operating System :: Microsoft :: Windows",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand Down Expand Up @@ -134,7 +133,7 @@ dirty_template = "{tag}+{ccount}.g{sha}.dirty"

[tool.black]
line-length = 100
target-version = ["py39", "py310", "py311", "py312"]
target-version = ["py310", "py311", "py312"]

[tool.isort]
sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
Expand Down Expand Up @@ -177,7 +176,7 @@ exclude_lines = [
[tool.ruff]
# https://github.com/charliermarsh/ruff/
line-length = 100
target-version = "py39"
target-version = "py310"
unfixable = [
"F841" # unused-variable (Note: can leave useless expression)
]
Expand Down Expand Up @@ -205,6 +204,7 @@ ignore = [
# "SIM401", # Use dict.get ... instead of if-else-block (Note: if-else better for coverage and sometimes clearer)
# "TRY004", # Prefer `TypeError` exception for invalid type (Note: good advice, but not worth the nuisance)
# "TRY200", # Use `raise from` to specify exception cause (Note: sometimes okay to raise original exception)
"UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)` (Note: using `|` seems to be slower)

# Intentionally ignored
"COM812", # Trailing comma missing
Expand Down
2 changes: 1 addition & 1 deletion scripts/scipy_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def pagerank(
err = np.absolute(x - xlast).sum()
if err < N * tol:
return x
# return dict(zip(nodelist, map(float, x)))
# return dict(zip(nodelist, map(float, x), strict=True))
raise nx.PowerIterationFailedConvergence(max_iter)


Expand Down
Loading