diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ddb84d8a0f0..542e9cacb77 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -63,3 +63,23 @@ repos: [.]flake8[.]cython$| meta[.]yaml$| setup[.]cfg$ + - repo: local + hooks: + - id: nx-cugraph-meta-data-update + name: nx-cugraph meta-data updater + entry: bash -c "PYTHONPATH=./python/nx-cugraph python ./python/nx-cugraph/_nx_cugraph/__init__.py" + files: ^python/nx-cugraph/ + types: [python] + language: python + pass_filenames: false + additional_dependencies: ["networkx>=3.2"] + - repo: local + hooks: + - id: nx-cugraph-readme-update + name: nx-cugraph README updater + entry: bash -c "PYTHONPATH=./python/nx-cugraph python ./python/nx-cugraph/scripts/update_readme.py ./python/nx-cugraph/README.md" + files: ^python/nx-cugraph/ + types_or: [python, markdown] + language: python + pass_filenames: false + additional_dependencies: ["networkx>=3.2"] diff --git a/ci/test_python.sh b/ci/test_python.sh index e05160239ab..3a47d7e1490 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -127,13 +127,6 @@ python -m nx_cugraph.scripts.print_tree --dispatch-name --plc --incomplete --dif python -m nx_cugraph.scripts.print_table popd -rapids-logger "ensure nx-cugraph autogenerated files are up to date" -pushd python/nx-cugraph -make || true -git diff --exit-code . -git checkout . -popd - rapids-logger "pytest cugraph-service (single GPU)" ./ci/run_cugraph_service_pytests.sh \ --verbose \ diff --git a/python/nx-cugraph/README.md b/python/nx-cugraph/README.md index 1bf310c8c88..77066356a4b 100644 --- a/python/nx-cugraph/README.md +++ b/python/nx-cugraph/README.md @@ -7,11 +7,10 @@ to run supported algorithms with GPU acceleration. ## System Requirements nx-cugraph requires the following: - - * NVIDIA GPU, Pascal architecture or later + * NVIDIA GPU, Volta architecture or later, with [compute capability](https://developer.nvidia.com/cuda-gpus) 7.0+ * CUDA 11.2, 11.4, 11.5, 11.8, or 12.0 - * Python versions 3.9, 3.10, or 3.11 - * NetworkX >= version 3.2 + * Python version 3.9, 3.10, or 3.11 + * NetworkX >= version 3.0 (version 3.2 or higher recommended) More details about system requirements can be found in the [RAPIDS System Requirements documentation](https://docs.rapids.ai/install#system-req). @@ -20,16 +19,25 @@ More details about system requirements can be found in the [RAPIDS System Requir nx-cugraph can be installed using either conda or pip. ### conda +#### latest nightly version ``` conda install -c rapidsai-nightly -c conda-forge -c nvidia nx-cugraph ``` +#### latest stable version +``` +conda install -c rapidsai -c conda-forge -c nvidia nx-cugraph +``` ### pip +#### latest nightly version +``` +python -m pip install nx-cugraph-cu11 --extra-index-url https://pypi.anaconda.org/rapidsai-wheels-nightly/simple +``` +#### latest stable version ``` python -m pip install nx-cugraph-cu11 --extra-index-url https://pypi.nvidia.com ``` Notes: - - * Nightly wheel builds will not be available until the 23.12 release, therefore the index URL for the stable release version is being used in the pip install command above. + * The pip example above installs for CUDA 11. To install for CUDA 12, replace `-cu11` with `-cu12` * Additional information relevant to installing any RAPIDS package can be found [here](https://rapids.ai/#quick-start). ## Enabling nx-cugraph diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index bc7f63fcd49..e05876737d4 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -258,6 +258,22 @@ def get_info(): if __name__ == "__main__": from pathlib import Path + # This script imports nx_cugraph modules, which imports nx_cugraph runtime + # dependencies. The modules do not need the runtime deps, so stub them out + # to avoid installing them. + class Stub: + def __getattr__(self, *args, **kwargs): + return Stub() + + def __call__(self, *args, **kwargs): + return Stub() + + import sys + + sys.modules["cupy"] = Stub() + sys.modules["numpy"] = Stub() + sys.modules["pylibcugraph"] = Stub() + from _nx_cugraph.core import main filepath = Path(__file__) diff --git a/python/nx-cugraph/_nx_cugraph/core.py b/python/nx-cugraph/_nx_cugraph/core.py index c4de197f3b1..82ce7bc438a 100644 --- a/python/nx-cugraph/_nx_cugraph/core.py +++ b/python/nx-cugraph/_nx_cugraph/core.py @@ -45,18 +45,27 @@ def update_text(text, lines_to_add, target, indent=" " * 8): return f"{text[:start]}{begin}{to_add}\n{indent}{text[stop:]}" +def dq_repr(s): + """Return repr(s) quoted with the double quote preference used by black.""" + rs = repr(s) + if rs.startswith("'") and '"' not in rs: + rs = rs.strip("'") + return f'"{rs}"' + return rs + + def dict_to_lines(d, *, indent=""): for key in sorted(d): val = d[key] if "\n" not in val: - yield f"{indent}{key!r}: {val!r}," + yield f"{indent}{dq_repr(key)}: {dq_repr(val)}," else: - yield f"{indent}{key!r}: (" + yield f"{indent}{dq_repr(key)}: (" *lines, last_line = val.split("\n") for line in lines: line += "\n" - yield f" {indent}{line!r}" - yield f" {indent}{last_line!r}" + yield f" {indent}{dq_repr(line)}" + yield f" {indent}{dq_repr(last_line)}" yield f"{indent})," @@ -83,7 +92,7 @@ def main(filepath): to_add = [] for name in sorted(additional_parameters): params = additional_parameters[name] - to_add.append(f"{name!r}: {{") + to_add.append(f"{dq_repr(name)}: {{") to_add.extend(dict_to_lines(params, indent=" " * 4)) to_add.append("},") text = update_text(text, to_add, "additional_parameters") diff --git a/python/nx-cugraph/scripts/update_readme.py b/python/nx-cugraph/scripts/update_readme.py index 1ab5a76c4c0..fcaa1769d8b 100755 --- a/python/nx-cugraph/scripts/update_readme.py +++ b/python/nx-cugraph/scripts/update_readme.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (c) 2024, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,12 +12,13 @@ # limitations under the License. import argparse import re +import urllib.request import zlib from collections import namedtuple from pathlib import Path from warnings import warn -from nx_cugraph.scripts.print_tree import create_tree, tree_lines +_objs_file_url = "https://networkx.org/documentation/stable/objects.inv" # See: https://sphobjinv.readthedocs.io/en/stable/syntax.html DocObject = namedtuple( @@ -75,6 +75,8 @@ def replace_body(text, match, new_body): def main(readme_file, objects_filename): """``readme_file`` must be readable and writable, so use mode ``"a+"``""" + from nx_cugraph.scripts.print_tree import create_tree, tree_lines + # Use the `objects.inv` file to determine URLs. For details about this file, see: # https://sphobjinv.readthedocs.io/en/stable/syntax.html # We might be better off using a library like that, but roll our own for now. @@ -190,14 +192,59 @@ def get_payload_internal(keys): return text +def find_or_download_objs_file(objs_file_dir): + """ + Returns the path to /objects.inv, downloading it from + _objs_file_url if it does not already exist. + """ + objs_file_path = objs_file_dir / "objects.inv" + if not objs_file_path.exists(): + request = urllib.request.Request(_objs_file_url) + with ( + urllib.request.urlopen(request) as response, + Path(objs_file_path).open("wb") as out, + ): + out.write(response.read()) + return objs_file_path + + if __name__ == "__main__": + # This script imports a nx_cugraph script module, which imports nx_cugraph + # runtime dependencies. The script module does not need the runtime deps, + # so stub them out to avoid installing them. + class Stub: + def __getattr__(self, *args, **kwargs): + return Stub() + + def __call__(self, *args, **kwargs): + return Stub() + + import sys + + sys.modules["cupy"] = Stub() + sys.modules["numpy"] = Stub() + sys.modules["pylibcugraph"] = Stub() + parser = argparse.ArgumentParser( "Update README.md to show NetworkX functions implemented by nx-cugraph" ) parser.add_argument("readme_filename", help="Path to the README.md file") parser.add_argument( - "networkx_objects", help="Path to the objects.inv file from networkx docs" + "networkx_objects", + nargs="?", + default=None, + help="Optional path to the objects.inv file from the NetworkX docs. Default is " + "the objects.inv file in the directory containing the specified README.md. If " + "an objects.inv file does not exist in that location, one will be downloaded " + "and saved to that location.", ) args = parser.parse_args() - with Path(args.readme_filename).open("a+") as readme_file: - main(readme_file, args.networkx_objects) + + readme_filename = args.readme_filename + readme_path = Path(readme_filename) + objects_filename = args.networkx_objects + if objects_filename is None: + objects_filename = find_or_download_objs_file(readme_path.parent) + + with readme_path.open("a+") as readme_file: + main(readme_file, objects_filename)