diff --git a/.gitignore b/.gitignore index 591fb98475304d03c5db75648a2cf04042d0dd5f..d74435d42cb30219f553e79083e16326a174d5f8 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,8 @@ doc/api/ dist/ .gitlab-ci-local/ cache/ +_citools/ +_work/ .cache/ .pytest_cache/ .mypy_cache/ -venv/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 275b47fd42a9f5a10ef6df9fa3c41546aff2da07..33ba40b6553c65c38298a4cdb2d22005bc7ea7fa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,5 +3,5 @@ # SPDX-License-Identifier: BSD-3-Clause include: - - project: bob/dev-profile + - project: software/dev-profile file: /gitlab/python.yml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b66bb6104d64095ea70f24d82852a1471effba0b..1f5fe1063f48c63d0dd3782ca4fffd41db08ebd8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,23 +6,23 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 23.1.0 hooks: - id: black - repo: https://github.com/pycqa/docformatter - rev: v1.5.0 + rev: v1.6.0.rc1 hooks: - id: docformatter - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.12.0 hooks: - id: isort - repo: https://github.com/pycqa/flake8 - rev: 5.0.4 + rev: 6.0.0 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.991 + rev: v1.0.0 hooks: - id: mypy args: [ @@ -32,12 +32,12 @@ repos: --ignore-missing-imports, ] - repo: https://github.com/asottile/pyupgrade - rev: v3.2.2 + rev: v3.3.1 hooks: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: check-ast - id: check-added-large-files @@ -50,6 +50,6 @@ repos: - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/fsfe/reuse-tool - rev: v1.0.0 + rev: v1.1.2 hooks: - id: reuse diff --git a/pyproject.toml b/pyproject.toml index 30f3ca4d85742d6eff6e17b95488d42f473acb9f..ecaecfd3e01ed212f22d750951ca1d4c8aa89c37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,8 +14,7 @@ description = "Automatic links direct project dependencies to the intersphinx ca dynamic = ["readme"] license = {text = "BSD 3-Clause License"} authors = [ - {name = "Andre Anjos"}, - {email = "andre.anjos@idiap.ch"}, + {name = "Andre Anjos", email = "andre.anjos@idiap.ch"}, ] classifiers = [ "Framework :: Sphinx :: Extension", diff --git a/src/auto_intersphinx/__init__.py b/src/auto_intersphinx/__init__.py index 5640780bfcfa434a5a901f0fa21b497788158139..312a11d56e0a1e4c521b2dda9ca77a56505c282d 100644 --- a/src/auto_intersphinx/__init__.py +++ b/src/auto_intersphinx/__init__.py @@ -44,7 +44,6 @@ def oneliner(s: str) -> str: A single line with all text. """ - return inspect.cleandoc(s).replace("\n", " ") @@ -64,7 +63,6 @@ def rewrap(s: str) -> str: An 80-column wrapped multiline string """ - return "\n".join(textwrap.wrap(oneliner(s), width=80)) @@ -146,7 +144,6 @@ def populate_intersphinx_mapping(app: Sphinx, config: Config) -> None: config: Sphinx configuration """ - m = config.intersphinx_mapping builtin_catalog = Catalog() @@ -307,7 +304,6 @@ def setup(app: Sphinx) -> dict[str, typing.Any]: extensions to be loaded, the loading relative order of this extension, and configuration options with their own defaults. """ - # we need intersphinx app.setup_extension("sphinx.ext.intersphinx") diff --git a/src/auto_intersphinx/catalog.py b/src/auto_intersphinx/catalog.py index a57cb16435d20ec83421a3743023a7d67cdb6ad4..a7ac671b38f92eca06f257d85b4a398c42e8de09 100644 --- a/src/auto_intersphinx/catalog.py +++ b/src/auto_intersphinx/catalog.py @@ -31,19 +31,18 @@ PackageDictionaryType = dict[str, dict[str, str]] BUILTIN_CATALOG = importlib.resources.files(__package__).joinpath( "catalog.json" ) -"""Base name for the catalog file distributed with this package""" +"""Base name for the catalog file distributed with this package.""" PEP440_RE = re.compile( r"^\s*" + packaging.version.VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE, ) -"""Regular expression for matching PEP-440 version numbers""" +"""Regular expression for matching PEP-440 version numbers.""" def _ensure_webdir(addr: str) -> str: """Ensures the web-address ends in a /, and contains ``objects.inv``""" - if addr.endswith(".html"): addr = addr[: addr.rfind("/")] if not addr.endswith("/"): @@ -59,7 +58,6 @@ def _ensure_webdir(addr: str) -> str: def _reorder_versions(vdict: dict[str, str]) -> dict[str, str]: """Re-orders version dictionary by decreasing version.""" - # nota bene: new dicts preserve insertion order retval: dict[str, str] = {} @@ -100,7 +98,6 @@ def docurls_from_environment(package: str) -> dict[str, str]: A dictionary, that maps the version of the documentation found on PyPI to the URL. """ - try: md = importlib.metadata.metadata(package) if md.get_all("Project-URL") is None: @@ -136,7 +133,6 @@ def docurls_from_rtd(package: str) -> dict[str, str]: for the given package on RTD. If the package's documentation is not available on RTD, returns an empty dictionary. """ - try: url = f"https://readthedocs.org/projects/{package}/versions/" logger.debug(f"Reaching for `{url}'...") @@ -159,6 +155,19 @@ def docurls_from_rtd(package: str) -> dict[str, str]: return {} +def _get_json(url: str) -> dict | None: + try: + logger.debug(f"Reaching for `{url}'...") + r = requests.get(url) + if r.ok: + return r.json() + + except requests.exceptions.RequestException: + pass + + return None + + def docurls_from_pypi(package: str, max_entries: int) -> dict[str, str]: """Checks PyPI for documentation pointers for a given package. @@ -184,20 +193,6 @@ def docurls_from_pypi(package: str, max_entries: int) -> dict[str, str]: A dictionary, that maps the version of the documentation found on PyPI to the URL. """ - - def _get_json(url: str) -> dict | None: - try: - - logger.debug(f"Reaching for `{url}'...") - r = requests.get(url) - if r.ok: - return r.json() - - except requests.exceptions.RequestException: - pass - - return None - versions: dict[str, str] = {} data = _get_json(f"https://pypi.org/pypi/{package}/json") if data is None: @@ -270,7 +265,6 @@ class Catalog(collections.abc.MutableMapping): def load(self, path: pathlib.Path) -> None: """Loads and replaces contents with those from the file.""" - with path.open("rt") as f: logger.debug(f"Loading package catalog from {str(path)}...") self._data = json.load(f) @@ -278,13 +272,11 @@ class Catalog(collections.abc.MutableMapping): def loads(self, contents: str) -> None: """Loads and replaces contents with those from the string.""" - self._data = json.loads(contents) logger.debug(f"Loaded {len(self)} entries from string") def dump(self, path: pathlib.Path) -> None: """Loads and replaces contents with those from the file.""" - if path.exists(): backup = path.with_suffix(path.suffix + "~") logger.debug(f"Backing up: {str(path)} -> {str(backup)}...") @@ -299,7 +291,6 @@ class Catalog(collections.abc.MutableMapping): def dumps(self) -> str: """Loads and replaces contents with those from the string.""" - return json.dumps(self._data, indent=2) def reset(self) -> None: @@ -327,7 +318,6 @@ class Catalog(collections.abc.MutableMapping): def _ensure_defaults(self, pkg: str) -> None: """Ensures a standardised setup for a package entry.""" - self.setdefault(pkg, {"versions": {}, "sources": {}}) self[pkg].setdefault("versions", {}) self[pkg].setdefault("sources", {}) @@ -395,7 +385,6 @@ class Catalog(collections.abc.MutableMapping): The dictionary of values for the current package, as obtained from readthedocs.org, and potentially merged with the existing one. """ - self._ensure_defaults(pkg) name = name or pkg @@ -512,9 +501,7 @@ class Catalog(collections.abc.MutableMapping): """ for pkg in pkgs: - for action in order: - if action == "environment": name = names.get(action, {}).get(pkg, pkg) if name is not None: @@ -543,7 +530,6 @@ class Catalog(collections.abc.MutableMapping): def self_update(self) -> None: """Runs a self-update procedure, by re-looking up known sources.""" - # organises the names as expected by update_versions() names: dict[str, dict[str, str]] = dict( environment={}, readthedocs={}, pypi={} @@ -575,7 +561,6 @@ def _string2version(v: str) -> packaging.version.Version | None: Either ``None``, or the version object with the parsed version. """ - v = v.replace(".x", "") try: return packaging.version.Version(v) @@ -605,7 +590,6 @@ def _prepare_versions(versions: dict[str, str]) -> dict[str, str]: A dictionary with keys that correspond to parsed versions and aliases. """ - if not versions: return versions @@ -678,15 +662,18 @@ class LookupCatalog: """Internally creates all possible aliases for package names and versions. - This method will expand the catalog package names and version numbers - so that the user can refer to these using environment, readthedocs.org - or pypi.org names for packages, and PEP-440 compatible strings for + This method will expand the catalog package names and version + numbers + so that the user can refer to these using environment, + readthedocs.org + or pypi.org names for packages, and PEP-440 compatible strings + for version names during the lookup. - The catalog associated to this lookup is not modified in this process. + The catalog associated to this lookup is not modified in this + process. All augmentations are built-into the object instance. """ - self._version_map: dict[str, dict[str, str]] = {} self._package_map: dict[str, str] = {} for pkg in self._catalog.keys(): @@ -721,7 +708,6 @@ class LookupCatalog: If a match is found, returns the URL for the documentation. Otherwise, returns the ``default`` value. """ - if pkg not in self._package_map: return default if version not in self._version_map[pkg]: diff --git a/src/auto_intersphinx/check_packages.py b/src/auto_intersphinx/check_packages.py index 97a315fb0970b86c9b365be904ddb985adbbf19a..6b5f8c9797de77d5510e0b076a785da56a91033d 100644 --- a/src/auto_intersphinx/check_packages.py +++ b/src/auto_intersphinx/check_packages.py @@ -22,7 +22,6 @@ from .update_catalog import setup_verbosity def _main(args) -> None: """Main function, that actually executes the check-package command.""" - setup_verbosity(args.verbose) builtin_catalog = Catalog() @@ -37,7 +36,6 @@ def _main(args) -> None: user_catalog.load(args.user) for p in args.packages: - if p in user_catalog: print(f"Found {p} in user catalog:") print( @@ -88,7 +86,6 @@ def _main(args) -> None: def add_parser(subparsers) -> argparse.ArgumentParser: """Just sets up the parser for this CLI.""" - prog = "auto-intersphinx check-packages" parser = subparsers.add_parser( diff --git a/src/auto_intersphinx/cli.py b/src/auto_intersphinx/cli.py index 28c9d59d0cd81d1ff2713f2af9db9e0ea4751d3a..2cc96c1c9353361524a6a896123d69ea63500d28 100644 --- a/src/auto_intersphinx/cli.py +++ b/src/auto_intersphinx/cli.py @@ -10,7 +10,6 @@ import sys def make_parser() -> argparse.ArgumentParser: """Creates the main parser.""" - parser = argparse.ArgumentParser( prog=sys.argv[0], description="Commands to handle sphinx catalogs.", diff --git a/src/auto_intersphinx/dump_objects.py b/src/auto_intersphinx/dump_objects.py index 9cb82a308cb21a5ae461ada99bc84cb8dbea4db3..2c0e8442a92e2727afad7f879cbd326e882868b8 100644 --- a/src/auto_intersphinx/dump_objects.py +++ b/src/auto_intersphinx/dump_objects.py @@ -16,7 +16,6 @@ logger = logging.getLogger(__name__) def _main(args) -> None: """Main function, that actually executes the dump-objects command.""" - setup_verbosity(args.verbose) from sphinx.ext import intersphinx @@ -28,7 +27,6 @@ def _main(args) -> None: def add_parser(subparsers) -> argparse.ArgumentParser: """Just sets up the parser for this CLI.""" - prog = "auto-intersphinx dump-objects" parser = subparsers.add_parser( diff --git a/src/auto_intersphinx/update_catalog.py b/src/auto_intersphinx/update_catalog.py index 95be815be5d94008aa87f0320b089b630eb32e89..842a5a783a987bcb31ed6b02524cd175fe321fca 100644 --- a/src/auto_intersphinx/update_catalog.py +++ b/src/auto_intersphinx/update_catalog.py @@ -24,7 +24,6 @@ logger = logging.getLogger(__name__) def _parse_requirements(contents: str) -> list[str]: """Parses a pip-requirements file and extracts package lists.""" - lines = contents.split() lines = [k.strip() for k in lines if not k.strip().startswith("#")] split_re = re.compile(r"[=\s]+") @@ -33,7 +32,6 @@ def _parse_requirements(contents: str) -> list[str]: def setup_verbosity(verbose: int) -> None: """Sets up logger verbosity.""" - import logging as builtin_logging package_logger = builtin_logging.getLogger("sphinx." + __package__) @@ -56,7 +54,6 @@ def setup_verbosity(verbose: int) -> None: def _main(args) -> None: """Main function, that actually executes the update-catalog command.""" - setup_verbosity(args.verbose) catalog = Catalog() @@ -78,7 +75,6 @@ def _main(args) -> None: package_list = [] for pkg in args.packages: - if pkg.startswith("http"): logger.info(f"Retrieving package list from `{pkg}'...") r = requests.get(pkg) @@ -112,7 +108,6 @@ def _main(args) -> None: def add_parser(subparsers) -> argparse.ArgumentParser: """Just sets up the parser for this CLI.""" - prog = "auto-intersphinx update-catalog" parser = subparsers.add_parser( diff --git a/tests/conftest.py b/tests/conftest.py index 71032b8f36351dbea0b8471f22cc624da2a61912..ba675dcc3bc755ed5b6bed3fc44c555deb2580e4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,5 +10,4 @@ from pytest import fixture @fixture def datadir(request) -> pathlib.Path: """Returns the directory in which the test is sitting.""" - return pathlib.Path(request.module.__file__).parents[0] / "data"