Skip to content

Commit

Permalink
Drop Python 3.9, add 3.12; update pre-commit (#84)
Browse files Browse the repository at this point in the history
* Drop Python 3.9, add 3.12; update pre-commit

* Use latest networkx release, not dev version

* Add .codecov.yml
  • Loading branch information
eriknw authored Dec 13, 2023
1 parent 43c1731 commit 10788fa
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 45 deletions.
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,
# 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 = "[email protected]"},
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

0 comments on commit 10788fa

Please sign in to comment.