From 5503a05ef66f16d4d6e20ff48c83d707fcc02044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20D=C3=BCster?= Date: Fri, 29 Sep 2023 21:43:51 +0200 Subject: [PATCH 1/4] [checkupdates] Add more docstrings --- fdroidserver/checkupdates.py | 170 +++++++++++++++++++++++++++++++++-- 1 file changed, 161 insertions(+), 9 deletions(-) diff --git a/fdroidserver/checkupdates.py b/fdroidserver/checkupdates.py index d5a136ad..02e629f8 100644 --- a/fdroidserver/checkupdates.py +++ b/fdroidserver/checkupdates.py @@ -40,11 +40,32 @@ from . import net from .exception import VCSException, NoSubmodulesException, FDroidException, MetaDataException -# Check for a new version by looking at a document retrieved via HTTP. -# The app's Update Check Data field is used to provide the information -# required. def check_http(app): + """Check for a new version by looking at a document retrieved via HTTP. + The app's UpdateCheckData field is used to provide the information + required. + + Parameters + ---------- + app : metadata.App + The App instance to check for updates for. + + Returns + ------- + version : str or None + The found versionName or None if the versionName should be ignored + according to UpdateCheckIgnore. + vercode : int or None + The found versionCode or None if the versionCode should be ignored + according to UpdateCheckIgnore. + + Raises + ------ + FDroidException + If UpdateCheckData is missing or is an invalid URL or if there is no + match for the provided versionName or versionCode regex. + """ if not app.UpdateCheckData: raise FDroidException('Missing Update Check Data') @@ -91,6 +112,31 @@ def check_tags(app, pattern): Whether this can be used reliably or not depends on the development procedures used by the project's developers. Use it with caution, because it's inappropriate for many projects. + + Parameters + ---------- + app : metadata.App + The App instance to check for updates for. + pattern : str + The pattern a tag needs to match to be considered. + + Returns + ------- + versionName : str + The highest found versionName. + versionCode : int + The highest found versionCode. + ref : str + The Git reference, commit hash or tag name, of the highest found + versionName, versionCode. + + Raises + ------ + MetaDataException + If this function is not suitable for the RepoType of the app or + information is missing to perform this type of check. + FDroidException + If no matching tags or no information whatsoever could be found. """ if app.RepoType == 'srclib': build_dir = Path('build/srclib') / app.Repo @@ -230,6 +276,25 @@ def check_repomanifest(app, branch=None): Whether this can be used reliably or not depends on the development procedures used by the project's developers. Use it with caution, because it's inappropriate for many projects. + + Parameters + ---------- + app : metadata.App + The App instance to check for updates for. + branch : str + The VCS branch where to search for versionCode, versionName. + + Returns + ------- + versionName : str + The highest found versionName. + versionCode : int + The highest found versionCode. + + Raises + ------ + FDroidException + If no package id or no version information could be found. """ if app.RepoType == 'srclib': build_dir = Path('build/srclib') / app.Repo @@ -328,7 +393,7 @@ def check_gplay(app): def try_init_submodules(app, last_build, vcs): - """Try to init submodules if the last build entry used them. + """Try to init submodules if the last build entry uses them. They might have been removed from the app's repo in the meantime, so if we can't find any submodules we continue with the updates check. @@ -343,19 +408,44 @@ def try_init_submodules(app, last_build, vcs): logging.info("submodule broken for {}".format(_getappname(app))) -# Return all directories under startdir that contain any of the manifest -# files, and thus are probably an Android project. def dirs_with_manifest(startdir): + """Find directories containing a manifest file. + + Yield all directories under startdir that contain any of the manifest + files, and thus are probably an Android project. + + Parameters + ---------- + startdir : str + Directory to be walked down for search + + Yields + ------ + path : pathlib.Path or None + A directory that contains a manifest file of an Android project, None if + no directory could be found + """ for root, _dirs, files in os.walk(startdir): if any(m in files for m in [ 'AndroidManifest.xml', 'pom.xml', 'build.gradle', 'build.gradle.kts']): yield Path(root) -# Tries to find a new subdir starting from the root build_dir. Returns said -# subdir relative to the build dir if found, None otherwise. def possible_subdirs(app): + """Try to find a new subdir starting from the root build_dir. + Yields said subdir relative to the build dir if found, None otherwise. + + Parameters + ---------- + app : metadata.App + The app to check for subdirs + + Yields + ------ + subdir : pathlib.Path or None + A possible subdir, None if no subdir could be found + """ if app.RepoType == 'srclib': build_dir = Path('build/srclib') / app.Repo else: @@ -381,6 +471,24 @@ def _getcvname(app): def fetch_autoname(app, tag): + """Fetch AutoName. + + Get the to be displayed name of an app from the source code and adjust the + App instance in case it is different name has been found. + + Parameters + ---------- + app : metadata.App + The App instance to get the AutoName for. + tag : str + Tag to fetch AutoName at. + + Returns + ------- + commitmsg : str or None + Commit message about the name change. None in case checking for the + name is disabled, a VCSException occured or no name could be found. + """ if not app.RepoType or app.UpdateCheckMode in ('None', 'Static') \ or app.UpdateCheckName == "Ignore": return None @@ -419,6 +527,25 @@ def fetch_autoname(app, tag): def operate_vercode(operation, vercode): + """Calculate a new versionCode from a mathematical operation. + + Parameters + ---------- + operation : str + The operation to execute to get the new versionCode. + vercode : int + The versionCode for replacing "%c" in the operation. + + Returns + ------- + vercode : int + The new versionCode obtained by executing the operation. + + Raises + ------ + MetaDataException + If the operation is invalid. + """ if not common.VERCODE_OPERATION_RE.match(operation): raise MetaDataException(_('Invalid VercodeOperation: {field}') .format(field=operation)) @@ -430,8 +557,27 @@ def operate_vercode(operation, vercode): def checkupdates_app(app): + """Check for new versions and updated name of a single app. + + Also write back changes to the metadata file and create a Git commit if + requested. + + Parameters + ---------- + app : metadata.App + The app to check for updates for. + + Raises + ------ + MetaDataException + If the app has an invalid UpdateCheckMode or AutoUpdateMode. + FDroidException + If no version information could be found, the current version is newer + than the found version, auto-update was requested but an app has no + CurrentVersionCode or (Git) commiting the changes failed. + """ # If a change is made, commitmsg should be set to a description of it. - # Only if this is set will changes be written back to the metadata. + # Only if this is set, changes will be written back to the metadata. commitmsg = None tag = None @@ -577,6 +723,7 @@ def checkupdates_app(app): def get_last_build_from_app(app): + """Get the last build entry of an app.""" if app.get('Builds'): return app['Builds'][-1] else: @@ -600,6 +747,11 @@ start_timestamp = time.gmtime() def main(): + """Check for updates for one or more apps. + + The behaviour of this function is influenced by the configuration file as + well as command line parameters. + """ global config, options # Parse command line... From 7b715bb2c9b726b34d8e119ae4cd666623487c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20D=C3=BCster?= Date: Sun, 22 Oct 2023 20:43:52 +0200 Subject: [PATCH 2/4] [docs] Enable intersphinx --- docs/source/conf.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index a6091df9..382d8feb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -30,6 +30,7 @@ extensions = [ 'numpydoc', 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', + "sphinx.ext.intersphinx", ] # Add any paths that contain templates here, relative to this directory. @@ -71,5 +72,6 @@ html_sidebars = { html_split_index = True #numpydoc_validation_checks = {"all"} - - +intersphinx_mapping = { + "python": ("https://docs.python.org/3/", None), +} From f23847085546c88e67202f2e1d85df167ec4cd8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20D=C3=BCster?= Date: Sun, 22 Oct 2023 21:01:27 +0200 Subject: [PATCH 3/4] [docs] Put type annotations in type hints instead of docstring --- fdroidserver/checkupdates.py | 69 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/fdroidserver/checkupdates.py b/fdroidserver/checkupdates.py index 02e629f8..a0ebe177 100644 --- a/fdroidserver/checkupdates.py +++ b/fdroidserver/checkupdates.py @@ -32,6 +32,7 @@ import logging import copy import urllib.parse from pathlib import Path +from typing import Optional, Union from . import _ from . import common @@ -40,7 +41,7 @@ from . import net from .exception import VCSException, NoSubmodulesException, FDroidException, MetaDataException -def check_http(app): +def check_http(app: metadata.App) -> tuple[Union[str, None], Union[int, None]]: """Check for a new version by looking at a document retrieved via HTTP. The app's UpdateCheckData field is used to provide the information @@ -48,15 +49,15 @@ def check_http(app): Parameters ---------- - app : metadata.App + app The App instance to check for updates for. Returns ------- - version : str or None + version The found versionName or None if the versionName should be ignored according to UpdateCheckIgnore. - vercode : int or None + vercode The found versionCode or None if the versionCode should be ignored according to UpdateCheckIgnore. @@ -106,7 +107,7 @@ def check_http(app): return (version, vercode) -def check_tags(app, pattern): +def check_tags(app: metadata.App, pattern: str) -> tuple[str, int, str]: """Check for a new version by looking at the tags in the source repo. Whether this can be used reliably or not depends on @@ -115,18 +116,18 @@ def check_tags(app, pattern): Parameters ---------- - app : metadata.App + app The App instance to check for updates for. - pattern : str + pattern The pattern a tag needs to match to be considered. Returns ------- - versionName : str + versionName The highest found versionName. - versionCode : int + versionCode The highest found versionCode. - ref : str + ref The Git reference, commit hash or tag name, of the highest found versionName, versionCode. @@ -270,7 +271,7 @@ def check_tags(app, pattern): raise FDroidException(_("Couldn't find any version information")) -def check_repomanifest(app, branch=None): +def check_repomanifest(app: metadata.App, branch: Optional[str] = None) -> tuple[str, int]: """Check for a new version by looking at the AndroidManifest.xml at the HEAD of the source repo. Whether this can be used reliably or not depends on @@ -279,16 +280,16 @@ def check_repomanifest(app, branch=None): Parameters ---------- - app : metadata.App + app The App instance to check for updates for. - branch : str + branch The VCS branch where to search for versionCode, versionName. Returns ------- - versionName : str + versionName The highest found versionName. - versionCode : int + versionCode The highest found versionCode. Raises @@ -392,7 +393,7 @@ def check_gplay(app): return (version.strip(), None) -def try_init_submodules(app, last_build, vcs): +def try_init_submodules(app: metadata.App, last_build: metadata.Build, vcs: common.vcs): """Try to init submodules if the last build entry uses them. They might have been removed from the app's repo in the meantime, @@ -408,7 +409,7 @@ def try_init_submodules(app, last_build, vcs): logging.info("submodule broken for {}".format(_getappname(app))) -def dirs_with_manifest(startdir): +def dirs_with_manifest(startdir: str): """Find directories containing a manifest file. Yield all directories under startdir that contain any of the manifest @@ -416,7 +417,7 @@ def dirs_with_manifest(startdir): Parameters ---------- - startdir : str + startdir Directory to be walked down for search Yields @@ -431,14 +432,14 @@ def dirs_with_manifest(startdir): yield Path(root) -def possible_subdirs(app): +def possible_subdirs(app: metadata.App): """Try to find a new subdir starting from the root build_dir. Yields said subdir relative to the build dir if found, None otherwise. Parameters ---------- - app : metadata.App + app The app to check for subdirs Yields @@ -462,15 +463,15 @@ def possible_subdirs(app): yield subdir -def _getappname(app): +def _getappname(app: metadata.App) -> str: return common.get_app_display_name(app) -def _getcvname(app): +def _getcvname(app: metadata.App) -> str: return '%s (%s)' % (app.CurrentVersion, app.CurrentVersionCode) -def fetch_autoname(app, tag): +def fetch_autoname(app: metadata.App, tag: str) -> Optional[str]: """Fetch AutoName. Get the to be displayed name of an app from the source code and adjust the @@ -478,14 +479,14 @@ def fetch_autoname(app, tag): Parameters ---------- - app : metadata.App + app The App instance to get the AutoName for. - tag : str + tag Tag to fetch AutoName at. Returns ------- - commitmsg : str or None + commitmsg Commit message about the name change. None in case checking for the name is disabled, a VCSException occured or no name could be found. """ @@ -526,19 +527,19 @@ def fetch_autoname(app, tag): return commitmsg -def operate_vercode(operation, vercode): +def operate_vercode(operation: str, vercode: int) -> int: """Calculate a new versionCode from a mathematical operation. Parameters ---------- - operation : str + operation The operation to execute to get the new versionCode. - vercode : int + vercode The versionCode for replacing "%c" in the operation. Returns ------- - vercode : int + vercode The new versionCode obtained by executing the operation. Raises @@ -556,7 +557,7 @@ def operate_vercode(operation, vercode): return vercode -def checkupdates_app(app): +def checkupdates_app(app: metadata.App) -> None: """Check for new versions and updated name of a single app. Also write back changes to the metadata file and create a Git commit if @@ -564,7 +565,7 @@ def checkupdates_app(app): Parameters ---------- - app : metadata.App + app The app to check for updates for. Raises @@ -722,7 +723,7 @@ def checkupdates_app(app): raise FDroidException("Git commit failed") -def get_last_build_from_app(app): +def get_last_build_from_app(app: metadata.App) -> metadata.Build: """Get the last build entry of an app.""" if app.get('Builds'): return app['Builds'][-1] @@ -730,7 +731,7 @@ def get_last_build_from_app(app): return metadata.Build() -def status_update_json(processed, failed): +def status_update_json(processed: list, failed: dict) -> None: """Output a JSON file with metadata about this run.""" logging.debug(_('Outputting JSON')) output = common.setup_status_output(start_timestamp) From 02b855da5eb49260d3867fdf38bf2e1225592c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20D=C3=BCster?= Date: Sun, 22 Oct 2023 21:27:08 +0200 Subject: [PATCH 4/4] [docs] Start utilising intersphinx --- fdroidserver/checkupdates.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/fdroidserver/checkupdates.py b/fdroidserver/checkupdates.py index a0ebe177..a67a4463 100644 --- a/fdroidserver/checkupdates.py +++ b/fdroidserver/checkupdates.py @@ -63,7 +63,7 @@ def check_http(app: metadata.App) -> tuple[Union[str, None], Union[int, None]]: Raises ------ - FDroidException + :exc:`~fdroidserver.exception.FDroidException` If UpdateCheckData is missing or is an invalid URL or if there is no match for the provided versionName or versionCode regex. """ @@ -133,10 +133,10 @@ def check_tags(app: metadata.App, pattern: str) -> tuple[str, int, str]: Raises ------ - MetaDataException + :exc:`~fdroidserver.exception.MetaDataException` If this function is not suitable for the RepoType of the app or information is missing to perform this type of check. - FDroidException + :exc:`~fdroidserver.exception.FDroidException` If no matching tags or no information whatsoever could be found. """ if app.RepoType == 'srclib': @@ -294,7 +294,7 @@ def check_repomanifest(app: metadata.App, branch: Optional[str] = None) -> tuple Raises ------ - FDroidException + :exc:`~fdroidserver.exception.FDroidException` If no package id or no version information could be found. """ if app.RepoType == 'srclib': @@ -422,7 +422,7 @@ def dirs_with_manifest(startdir: str): Yields ------ - path : pathlib.Path or None + path : :class:`pathlib.Path` or None A directory that contains a manifest file of an Android project, None if no directory could be found """ @@ -444,7 +444,7 @@ def possible_subdirs(app: metadata.App): Yields ------ - subdir : pathlib.Path or None + subdir : :class:`pathlib.Path` or None A possible subdir, None if no subdir could be found """ if app.RepoType == 'srclib': @@ -544,7 +544,7 @@ def operate_vercode(operation: str, vercode: int) -> int: Raises ------ - MetaDataException + :exc:`~fdroidserver.exception.MetaDataException` If the operation is invalid. """ if not common.VERCODE_OPERATION_RE.match(operation): @@ -570,9 +570,9 @@ def checkupdates_app(app: metadata.App) -> None: Raises ------ - MetaDataException + :exc:`~fdroidserver.exception.MetaDataException` If the app has an invalid UpdateCheckMode or AutoUpdateMode. - FDroidException + :exc:`~fdroidserver.exception.FDroidException` If no version information could be found, the current version is newer than the found version, auto-update was requested but an app has no CurrentVersionCode or (Git) commiting the changes failed.