From d65d4376fdecf91cd1c35a8ca093b276acf20ced Mon Sep 17 00:00:00 2001 From: s-weigand Date: Fri, 10 Jan 2020 18:06:45 +0100 Subject: [PATCH 01/13] created command line tool to create all html theme docs at once --- .gitignore | 6 + dev_utils/theme_builder.py | 377 +++++++++++++++++++++++++++++++++++++ 2 files changed, 383 insertions(+) create mode 100644 dev_utils/theme_builder.py diff --git a/.gitignore b/.gitignore index 24da0617..e5bbe23e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,9 @@ nbsphinx.egg-info/ .python-version .vscode doc/_build + +dev_utils/*.rst +dev_utils/doc +dev_utils/_build +dev_utils/temp_git +dev_utils/theme_requirements.txt diff --git a/dev_utils/theme_builder.py b/dev_utils/theme_builder.py new file mode 100644 index 00000000..aaafccaf --- /dev/null +++ b/dev_utils/theme_builder.py @@ -0,0 +1,377 @@ +import argparse +from difflib import ndiff +from pprint import pformat +import shutil +import sys +import os + + +import git +from sphinx.cmd.build import build_main + +REPO_ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +ROOT_DOC_DIR = os.path.join(REPO_ROOT_DIR, "doc") + +DEV_UTILS_DIR = os.path.join(REPO_ROOT_DIR, "dev_utils") +TEMP_GIT_DIR = os.path.join(DEV_UTILS_DIR, "temp_git") +TEMP_DOC_DIR = os.path.join(DEV_UTILS_DIR, "doc") +TEMP_BUILD_ROOT_DIR = os.path.join(DEV_UTILS_DIR, "_build") + + +def format_list(input_list): + """ + Pretty formats list for printing. + + Parameters + ---------- + input_list : List + List to be printed. + + Returns + ------- + str + Pretty formated list. + """ + return pformat(input_list, compact=True) + + +class InvalidThemeException(Exception): + def __init__(self, invalid_themes, valid_themes): + """ + Exception thrown when a theme name given via the cli isn't supported. + + Parameters + ---------- + invalid_themes : List[str] + List of invalid theme names. + valid_themes : List[str] + List of valid theme names. + """ + super().__init__( + "\nYou used the following invalid theme names:\n{}" + "\nThe valid theme names are:\n{}".format( + format_list(invalid_themes), format_list(["all"] + valid_themes) + ) + ) + + +class ThemeBuilder: + """ + Class to interact with git and build the html docs + for the different theme branches. + """ + + def __init__(self): + self.prepare_git() + self.theme_branche_refs = self.get_theme_branch_refs() + self.master_commit = self.repo.commit("{}/master".format(self.remote.name)) + self.build_args = [] + + def prepare_git(self): + """ + Creates a temporary git repository, which is used to the the difference + of theme branches and master 'doc/conf.py' and 'doc/requirements.txt'. + """ + if os.path.isdir(TEMP_GIT_DIR): + self.repo = git.Repo(TEMP_GIT_DIR) + else: + self.repo = git.Repo.init(TEMP_GIT_DIR) + + remote_name = "upstream_diff_repo" + + if remote_name not in self.repo.remotes: + self.remote = self.repo.create_remote( + remote_name, "git@github.com:spatialaudio/nbsphinx.git" + ) + else: + self.remote = self.repo.remotes[remote_name] + self.remote.fetch() + self.repo.create_head("master", self.remote.refs.master) + + def get_theme_branch_refs(self): + """ + Returns the list of theme branch references. + + Returns + ------- + List[str] + List of branch refs that end with '-theme' + """ + theme_branche_refs = [] + for remote_brach in self.remote.refs: + if remote_brach.name.endswith("-theme"): + theme_branche_refs.append(remote_brach.name) + return theme_branche_refs + + def get_diff_string(self, branch_ref, file_path): + """ + Extracts the diff of the file at file_path the brach branch_ref + and the same file at master. + + Parameters + ---------- + branch_ref : str + Brach reference of the branch which should be compared to master. + file_path : str + Path of the file which diff should be extracted. + + Returns + ------- + str + Diff of the file on branch_ref and master + """ + theme_branch_commit = self.repo.commit(branch_ref) + diff_index = self.master_commit.diff(theme_branch_commit) + for diff_item in diff_index.iter_change_type("M"): + if diff_item.a_path == diff_item.b_path and diff_item.a_path == file_path: + + master_blob = ( + diff_item.a_blob.data_stream.read() + .decode("utf-8") + .splitlines(keepends=True) + ) + theme_blob = ( + diff_item.b_blob.data_stream.read() + .decode("utf-8") + .splitlines(keepends=True) + ) + diff = ndiff(master_blob, theme_blob) + return self.diff_to_string(diff) + return "" + + @staticmethod + def diff_to_string(diff): + """ + Converts a diff generated by ndiff to a string, + only consisting of the different lines. + + Parameters + ---------- + diff : Differ-style delta + diff generated by ndiff + + Returns + ------- + str + String containing only the different lines. + """ + diff_list = [ + diff_line.replace("+ ", "", 1) + for diff_line in diff + if diff_line.startswith("+ ") + ] + if len(diff_list): + return "".join(diff_list) + else: + return "" + + def get_theme_requirements(self): + """ + Reads all the diff of 'doc/requirements.txt' for all theme branches + and writes them to 'dev_utils/theme_requirements.txt', + so they can be installed all at once. + """ + print( + "Building new 'dev_utils/theme_requirements.txt'.\n" + "Make sure that all requirements are installed by running:\n" + "'pip install -r dev_utils/theme_requirements.txt'" + "from the repo root." + ) + # the replace is a hack for windows paths + theme_requirement_list = [ + "-r {}".format( + os.path.join(ROOT_DOC_DIR, "requirements.txt").replace("\\", "/") + "\n" + ) + ] + for theme_branche_ref in self.theme_branche_refs: + theme_requirement_list.append( + self.get_diff_string(theme_branche_ref, "doc/requirements.txt") + ) + requirement_file_path = os.path.join(DEV_UTILS_DIR, "theme_requirements.txt") + with open(requirement_file_path, "w") as theme_requirements: + theme_requirements.write("".join(theme_requirement_list)) + + @staticmethod + def copy_root_docs(): + """ + Copyes all needed files to build the docs from the repository root + to the corresponding position in 'DEV_UTILS_DIR'. + """ + shutil.rmtree(TEMP_DOC_DIR, ignore_errors=True) + shutil.copytree(ROOT_DOC_DIR, TEMP_DOC_DIR) + for file_name in ["README.rst", "CONTRIBUTING.rst"]: + shutil.copyfile( + os.path.join(REPO_ROOT_DIR, file_name), + os.path.join(DEV_UTILS_DIR, file_name), + ) + + def update_theme_conf(self, branch_ref): + """ + Appends the diff on the theme branch to the master brach, + to the current 'doc/conf.py' + + Parameters + ---------- + branch_ref : str + Brach reference of a theme branch. + """ + conf_diff = self.get_diff_string(branch_ref, "doc/conf.py") + with open(os.path.join(ROOT_DOC_DIR, "conf.py")) as orig_conf_file: + orig_conf = orig_conf_file.read() + with open(os.path.join(TEMP_DOC_DIR, "conf.py"), "w") as temp_conf_file: + temp_conf_file.write(orig_conf + conf_diff) + + def build_theme(self, branch_ref): + """ + Build the html docs of the theme given by branch_ref. + + Parameters + ---------- + branch_ref : str + Brach reference of a theme branch. + + Raises + ------ + Exception + Exception raised when build_main exits with a none zero code. + i.e. KeyboardInterrupt, so theme build don't need to be chanceled one by one. + """ + branch_name = branch_ref.split("/")[1] + print("\n\n") + print("#" * 80) + print("#", "BUILDING BRANCH: {}".format(branch_name.upper()).center(76), "#") + print("#" * 80) + self.update_theme_conf(branch_ref) + dest_dir = os.path.join(TEMP_BUILD_ROOT_DIR, branch_name) + if build_main([TEMP_DOC_DIR, dest_dir] + self.build_args) != 0: + raise Exception("An Error occurred building the docs.") + + def ref_to_theme_name(self, branch_ref): + """ + Converts a branch_ref to the theme name. + + Parameters + ---------- + branch_ref : str + Brach reference of a theme branch. + + Returns + ------- + str + Theme name + """ + return branch_ref.split("/")[1].rstrip("-theme") + + def get_theme_names(self): + """ + Return a list of all valid theme names. + + Returns + ------- + List[str] + Valid theme names. + """ + theme_names = [ + self.ref_to_theme_name(theme_branch_ref) + for theme_branch_ref in self.theme_branche_refs + ] + return theme_names + + def validate_theme_list(self, theme_list): + """ + Validates theme_list and throws InvalidThemeException if 'all' + isn't in the list and any theme name isn't valid. + + Parameters + ---------- + theme_list : List[str] + List of theme names. + + Raises + ------ + InvalidThemeException + [description] + """ + valid_themes = self.get_theme_names() + invalid_themes = [] + if "all" in theme_list: + return + else: + for theme_name in theme_list: + if theme_name not in valid_themes: + invalid_themes.append(theme_name) + if len(invalid_themes): + raise InvalidThemeException(invalid_themes, valid_themes) + + def build_themes(self, theme_list): + """ + Builds the themes with the name provided by themelist. + + Parameters + ---------- + theme_list : List[str] + List of theme names. + """ + self.validate_theme_list(theme_list) + self.copy_root_docs() + for branch_ref in self.theme_branche_refs: + if "all" in theme_list: + self.build_theme(branch_ref) + elif self.ref_to_theme_name(branch_ref) in theme_list: + self.build_theme(branch_ref) + + +def cli(argv=sys.argv[1:]): + parser = argparse.ArgumentParser(usage="python %(prog)s [OPTIONS]",) + parser.add_argument( + "-t", + "--themes", + default="all", + dest="themes", + nargs="*", + help="List of theme names which should be build (default: 'all')", + ) + parser.add_argument( + "-a", + action="store_true", + dest="force_all", + help="write all files (default: only write new and changed files)", + ) + parser.add_argument( + "-l", + action="store_true", + dest="list_themes", + help="Lists all available themes and exits.", + ) + parser.add_argument( + "-r", + action="store_true", + dest="build_requirements", + help="Build the requirements file to build all theme.", + ) + args = parser.parse_args(argv) + theme_builder = ThemeBuilder() + + if args.list_themes: + print( + "The available themes are:\n{}".format( + format_list(theme_builder.get_theme_names()) + ) + ) + return + + if ( + not os.path.isfile(os.path.join(DEV_UTILS_DIR, "theme_requirements.txt")) + or args.build_requirements + ): + theme_builder.get_theme_requirements() + return + + if args.force_all: + theme_builder.build_args += ["-a"] + + theme_builder.build_themes(args.themes) + + +if __name__ == "__main__": + cli() From 08803a0a2aa695a7fb4f867b8ca3077baa5b04d1 Mon Sep 17 00:00:00 2001 From: s-weigand Date: Mon, 13 Jan 2020 02:23:40 +0100 Subject: [PATCH 02/13] refactored theme_builder file/folder creation, to be more compact --- .gitignore | 5 ++--- dev_utils/theme_builder.py | 40 ++++++++++++++++++++------------------ 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index e5bbe23e..6a08aec9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ nbsphinx.egg-info/ doc/_build dev_utils/*.rst -dev_utils/doc dev_utils/_build -dev_utils/temp_git -dev_utils/theme_requirements.txt +dev_utils/tmp +dev_utils/requirements_themes.txt diff --git a/dev_utils/theme_builder.py b/dev_utils/theme_builder.py index aaafccaf..afc53774 100644 --- a/dev_utils/theme_builder.py +++ b/dev_utils/theme_builder.py @@ -5,16 +5,23 @@ import sys import os +try: + import git +except ImportError as e: + print( + "'GitPython', which is needed to run this tool, isn't installed." + "To install it, run\n:'pip install GitPython'" + ) + raise e -import git from sphinx.cmd.build import build_main REPO_ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) ROOT_DOC_DIR = os.path.join(REPO_ROOT_DIR, "doc") DEV_UTILS_DIR = os.path.join(REPO_ROOT_DIR, "dev_utils") -TEMP_GIT_DIR = os.path.join(DEV_UTILS_DIR, "temp_git") -TEMP_DOC_DIR = os.path.join(DEV_UTILS_DIR, "doc") +TEMP_DIR = os.path.join(DEV_UTILS_DIR, "tmp") +TEMP_DOC_DIR = os.path.join(TEMP_DIR, "doc") TEMP_BUILD_ROOT_DIR = os.path.join(DEV_UTILS_DIR, "_build") @@ -72,10 +79,10 @@ def prepare_git(self): Creates a temporary git repository, which is used to the the difference of theme branches and master 'doc/conf.py' and 'doc/requirements.txt'. """ - if os.path.isdir(TEMP_GIT_DIR): - self.repo = git.Repo(TEMP_GIT_DIR) + if os.path.isdir(os.path.join(TEMP_DIR, ".git")): + self.repo = git.Repo(TEMP_DIR) else: - self.repo = git.Repo.init(TEMP_GIT_DIR) + self.repo = git.Repo.init(TEMP_DIR) remote_name = "upstream_diff_repo" @@ -172,22 +179,17 @@ def get_theme_requirements(self): so they can be installed all at once. """ print( - "Building new 'dev_utils/theme_requirements.txt'.\n" + "Building new 'dev_utils/requirements_themes.txt'.\n" "Make sure that all requirements are installed by running:\n" - "'pip install -r dev_utils/theme_requirements.txt'" + "'pip install -r dev_utils/requirements_themes.txt' " "from the repo root." ) - # the replace is a hack for windows paths - theme_requirement_list = [ - "-r {}".format( - os.path.join(ROOT_DOC_DIR, "requirements.txt").replace("\\", "/") + "\n" - ) - ] + theme_requirement_list = ["-r ../doc/requirements.txt\n"] for theme_branche_ref in self.theme_branche_refs: theme_requirement_list.append( self.get_diff_string(theme_branche_ref, "doc/requirements.txt") ) - requirement_file_path = os.path.join(DEV_UTILS_DIR, "theme_requirements.txt") + requirement_file_path = os.path.join(DEV_UTILS_DIR, "requirements_themes.txt") with open(requirement_file_path, "w") as theme_requirements: theme_requirements.write("".join(theme_requirement_list)) @@ -202,7 +204,7 @@ def copy_root_docs(): for file_name in ["README.rst", "CONTRIBUTING.rst"]: shutil.copyfile( os.path.join(REPO_ROOT_DIR, file_name), - os.path.join(DEV_UTILS_DIR, file_name), + os.path.join(TEMP_DIR, file_name), ) def update_theme_conf(self, branch_ref): @@ -335,13 +337,13 @@ def cli(argv=sys.argv[1:]): "-a", action="store_true", dest="force_all", - help="write all files (default: only write new and changed files)", + help="Write all files (default: only write new and changed files)", ) parser.add_argument( "-l", action="store_true", dest="list_themes", - help="Lists all available themes and exits.", + help="Show all available themes and exit.", ) parser.add_argument( "-r", @@ -361,7 +363,7 @@ def cli(argv=sys.argv[1:]): return if ( - not os.path.isfile(os.path.join(DEV_UTILS_DIR, "theme_requirements.txt")) + not os.path.isfile(os.path.join(DEV_UTILS_DIR, "requirements_themes.txt")) or args.build_requirements ): theme_builder.get_theme_requirements() From 8cc6dcf6ec2bf1373ab9b6ebf2b885171a028710 Mon Sep 17 00:00:00 2001 From: s-weigand Date: Mon, 13 Jan 2020 03:30:16 +0100 Subject: [PATCH 03/13] added guide on how to use theme_builder --- CONTRIBUTING.rst | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 55bfdff9..327e7a3e 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -43,5 +43,38 @@ Again, you'll probably have to use ``python`` instead of ``python3``. The generated files will be available in the directories ``build/sphinx/html/`` and ``build/sphinx/latex/``, respectively. +Building Themes +--------------- + +``nbsphinx`` supports over 30 different html themes, +with each having it's own branch ending in ``-theme``. +Those branches can on the one hand be used as an example reference +implementation of the given theme, on the other hand each theme is also +build as an own version on the `documentation of nbsphinx`_ and +thus can also be used a preview what this theme would look like. + +To simplify the building and testing of themes, +which is especially needed when changing CSS, +we provide you with command line tool to build all themes +or a user specified subset. +The tool is located at ``dev_utils/theme_builder.py`` can be run with:: + + python3 dev_utils/theme_builder.py + +On its first run, it will just create ``dev_utils/requirements_themes.txt`` +which contains the dependencies to build to build all themes and instructs +you how to install them. +And after it will by default build all supported themes. + +If you just want to build a subset of the themes +(i.e. ``alabaster`` and ``sphinx_rtd_theme``), simply run:: + + python3 dev_utils/theme_builder.py --themes alabaster rtd + +for more information run:: + + python3 dev_utils/theme_builder.py --help + .. _PyPI: https://pypi.org/project/nbsphinx/ .. _Github: https://github.com/spatialaudio/nbsphinx/ +.. _`documentation of nbsphinx`: http://nbsphinx.readthedocs.io/ From 879382f633ee25daf7a41d9a08f6a5efe9afad5d Mon Sep 17 00:00:00 2001 From: s-weigand Date: Mon, 13 Jan 2020 16:56:55 +0100 Subject: [PATCH 04/13] made ref_to_theme_name more reliable --- dev_utils/theme_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_utils/theme_builder.py b/dev_utils/theme_builder.py index afc53774..7198c9a5 100644 --- a/dev_utils/theme_builder.py +++ b/dev_utils/theme_builder.py @@ -262,7 +262,7 @@ def ref_to_theme_name(self, branch_ref): str Theme name """ - return branch_ref.split("/")[1].rstrip("-theme") + return branch_ref.split("/")[1].replace("-theme", "", 1) def get_theme_names(self): """ From 7bda7e36ba2183a5ca2790fe1b95feb1c4e2c073 Mon Sep 17 00:00:00 2001 From: s-weigand Date: Mon, 13 Jan 2020 17:01:05 +0100 Subject: [PATCH 05/13] added args for to use '-d' on building when possible to share build cache and reduce build time considerably, props go to https://github.com/spatialaudio/nbsphinx/issues/376#issuecomment-573634543 --- dev_utils/theme_builder.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dev_utils/theme_builder.py b/dev_utils/theme_builder.py index 7198c9a5..79c7c129 100644 --- a/dev_utils/theme_builder.py +++ b/dev_utils/theme_builder.py @@ -73,6 +73,7 @@ def __init__(self): self.theme_branche_refs = self.get_theme_branch_refs() self.master_commit = self.repo.commit("{}/master".format(self.remote.name)) self.build_args = [] + self.shared_cache_args = ["-d", os.path.join(TEMP_DIR, "shared_build_cache")] def prepare_git(self): """ @@ -245,7 +246,11 @@ def build_theme(self, branch_ref): print("#" * 80) self.update_theme_conf(branch_ref) dest_dir = os.path.join(TEMP_BUILD_ROOT_DIR, branch_name) - if build_main([TEMP_DOC_DIR, dest_dir] + self.build_args) != 0: + build_args = [TEMP_DOC_DIR, dest_dir] + self.build_args + # the theme "guzzle" and "press" need + if self.ref_to_theme_name(branch_ref) not in ["guzzle", "press"]: + build_args += self.shared_cache_args + if build_main(build_args) != 0: raise Exception("An Error occurred building the docs.") def ref_to_theme_name(self, branch_ref): From 0143351fed8a45c4950a9ae348146b2dcd66edda Mon Sep 17 00:00:00 2001 From: s-weigand Date: Mon, 13 Jan 2020 17:06:40 +0100 Subject: [PATCH 06/13] removed now outdated ignored files --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6a08aec9..4190d8db 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ nbsphinx.egg-info/ .vscode doc/_build -dev_utils/*.rst dev_utils/_build dev_utils/tmp dev_utils/requirements_themes.txt From 04c86035dc4bf1a4c103cb897f998478a296fd5a Mon Sep 17 00:00:00 2001 From: s-weigand Date: Mon, 13 Jan 2020 18:06:32 +0100 Subject: [PATCH 07/13] updated docstring of validate_theme_list --- dev_utils/theme_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_utils/theme_builder.py b/dev_utils/theme_builder.py index 79c7c129..aa69c5f3 100644 --- a/dev_utils/theme_builder.py +++ b/dev_utils/theme_builder.py @@ -297,7 +297,7 @@ def validate_theme_list(self, theme_list): Raises ------ InvalidThemeException - [description] + If a theme name provided in theme_list isn't valid. """ valid_themes = self.get_theme_names() invalid_themes = [] From 2b7dfebc7a2ad518e453879768c02cabc2c9b8c1 Mon Sep 17 00:00:00 2001 From: s-weigand Date: Wed, 15 Jan 2020 17:39:02 +0100 Subject: [PATCH 08/13] Changed the assumption to generate the diff from, theme branch differs to master, to theme branch has only one commit differing to its base --- dev_utils/theme_builder.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/dev_utils/theme_builder.py b/dev_utils/theme_builder.py index aa69c5f3..edb51f28 100644 --- a/dev_utils/theme_builder.py +++ b/dev_utils/theme_builder.py @@ -71,14 +71,13 @@ class ThemeBuilder: def __init__(self): self.prepare_git() self.theme_branche_refs = self.get_theme_branch_refs() - self.master_commit = self.repo.commit("{}/master".format(self.remote.name)) self.build_args = [] self.shared_cache_args = ["-d", os.path.join(TEMP_DIR, "shared_build_cache")] def prepare_git(self): """ - Creates a temporary git repository, which is used to the the difference - of theme branches and master 'doc/conf.py' and 'doc/requirements.txt'. + Creates a temporary git repository, which is used to extract + the difference of theme branches to their base branch. """ if os.path.isdir(os.path.join(TEMP_DIR, ".git")): self.repo = git.Repo(TEMP_DIR) @@ -113,27 +112,29 @@ def get_theme_branch_refs(self): def get_diff_string(self, branch_ref, file_path): """ - Extracts the diff of the file at file_path the brach branch_ref - and the same file at master. + Extracts the diff of the last commit for the file at file_path + on the brach branch_ref. Parameters ---------- branch_ref : str - Brach reference of the branch which should be compared to master. + Brach reference of a theme branch which should be compared to its base. file_path : str Path of the file which diff should be extracted. Returns ------- str - Diff of the file on branch_ref and master + Diff of the file on branch_ref and its base """ - theme_branch_commit = self.repo.commit(branch_ref) - diff_index = self.master_commit.diff(theme_branch_commit) + theme_specific_commit, base_branch_commit = list( + self.repo.iter_commits(branch_ref, max_count=2) + ) + diff_index = base_branch_commit.diff(theme_specific_commit) for diff_item in diff_index.iter_change_type("M"): if diff_item.a_path == diff_item.b_path and diff_item.a_path == file_path: - master_blob = ( + base_blob = ( diff_item.a_blob.data_stream.read() .decode("utf-8") .splitlines(keepends=True) @@ -143,7 +144,7 @@ def get_diff_string(self, branch_ref, file_path): .decode("utf-8") .splitlines(keepends=True) ) - diff = ndiff(master_blob, theme_blob) + diff = ndiff(base_blob, theme_blob) return self.diff_to_string(diff) return "" @@ -210,7 +211,7 @@ def copy_root_docs(): def update_theme_conf(self, branch_ref): """ - Appends the diff on the theme branch to the master brach, + Appends the diff of the theme branch to its base brach, to the current 'doc/conf.py' Parameters From 50bc49af6c2e3ab84519ae42752a9e2372d5cf1f Mon Sep 17 00:00:00 2001 From: s-weigand Date: Wed, 15 Jan 2020 18:00:57 +0100 Subject: [PATCH 09/13] changed explanation what theme branches are for --- CONTRIBUTING.rst | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 327e7a3e..54406d5d 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -46,12 +46,8 @@ and ``build/sphinx/latex/``, respectively. Building Themes --------------- -``nbsphinx`` supports over 30 different html themes, +The ``nbsphinx`` documentation is available in over 30 different `HTML themes`_, with each having it's own branch ending in ``-theme``. -Those branches can on the one hand be used as an example reference -implementation of the given theme, on the other hand each theme is also -build as an own version on the `documentation of nbsphinx`_ and -thus can also be used a preview what this theme would look like. To simplify the building and testing of themes, which is especially needed when changing CSS, @@ -77,4 +73,4 @@ for more information run:: .. _PyPI: https://pypi.org/project/nbsphinx/ .. _Github: https://github.com/spatialaudio/nbsphinx/ -.. _`documentation of nbsphinx`: http://nbsphinx.readthedocs.io/ +.. _`HTML themes`: https://nbsphinx.readthedocs.io/en/0.5.0/usage.html#HTML-Themes From c27198cb03a51e4f3a7a127d0d45796e5e260c73 Mon Sep 17 00:00:00 2001 From: s-weigand Date: Fri, 17 Jan 2020 16:57:47 +0100 Subject: [PATCH 10/13] Fixed typos in contributing guide --- CONTRIBUTING.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 54406d5d..abd39213 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -47,18 +47,18 @@ Building Themes --------------- The ``nbsphinx`` documentation is available in over 30 different `HTML themes`_, -with each having it's own branch ending in ``-theme``. +with each having its own branch ending in ``-theme``. To simplify the building and testing of themes, which is especially needed when changing CSS, we provide you with command line tool to build all themes or a user specified subset. -The tool is located at ``dev_utils/theme_builder.py`` can be run with:: +The tool is located at ``dev_utils/theme_builder.py`` and can be run with:: python3 dev_utils/theme_builder.py On its first run, it will just create ``dev_utils/requirements_themes.txt`` -which contains the dependencies to build to build all themes and instructs +which contains the dependencies to build all themes and instructs you how to install them. And after it will by default build all supported themes. @@ -73,4 +73,4 @@ for more information run:: .. _PyPI: https://pypi.org/project/nbsphinx/ .. _Github: https://github.com/spatialaudio/nbsphinx/ -.. _`HTML themes`: https://nbsphinx.readthedocs.io/en/0.5.0/usage.html#HTML-Themes +.. _`HTML themes`: https://nbsphinx.readthedocs.io/usage.html#HTML-Themes From 26618b125ebccb701219f8c0917351484a0d39bc Mon Sep 17 00:00:00 2001 From: s-weigand Date: Fri, 17 Jan 2020 18:59:06 +0100 Subject: [PATCH 11/13] Made fetching optional if the Repo exists --- dev_utils/theme_builder.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/dev_utils/theme_builder.py b/dev_utils/theme_builder.py index edb51f28..ba641711 100644 --- a/dev_utils/theme_builder.py +++ b/dev_utils/theme_builder.py @@ -79,10 +79,12 @@ def prepare_git(self): Creates a temporary git repository, which is used to extract the difference of theme branches to their base branch. """ + fetch_needed = False if os.path.isdir(os.path.join(TEMP_DIR, ".git")): self.repo = git.Repo(TEMP_DIR) else: self.repo = git.Repo.init(TEMP_DIR) + fetch_needed = True remote_name = "upstream_diff_repo" @@ -92,9 +94,17 @@ def prepare_git(self): ) else: self.remote = self.repo.remotes[remote_name] - self.remote.fetch() + + if fetch_needed: + self.fetch_remote() self.repo.create_head("master", self.remote.refs.master) + def fetch_remote(self): + """ + Fetches the remote refs + """ + self.remote.fetch() + def get_theme_branch_refs(self): """ Returns the list of theme branch references. @@ -357,9 +367,15 @@ def cli(argv=sys.argv[1:]): dest="build_requirements", help="Build the requirements file to build all theme.", ) + parser.add_argument( + "--fetch", action="store_true", dest="fetch", help="Fetch remote refs", + ) args = parser.parse_args(argv) theme_builder = ThemeBuilder() + if args.fetch: + theme_builder.fetch_remote() + if args.list_themes: print( "The available themes are:\n{}".format( From de2dd947f481f3d8802105e624ae4b9aaca1241c Mon Sep 17 00:00:00 2001 From: s-weigand Date: Fri, 17 Jan 2020 20:55:03 +0100 Subject: [PATCH 12/13] refactored diffing to only us GitPython --- dev_utils/theme_builder.py | 52 ++++++++------------------------------ 1 file changed, 10 insertions(+), 42 deletions(-) diff --git a/dev_utils/theme_builder.py b/dev_utils/theme_builder.py index ba641711..34a1f14b 100644 --- a/dev_utils/theme_builder.py +++ b/dev_utils/theme_builder.py @@ -1,5 +1,4 @@ import argparse -from difflib import ndiff from pprint import pformat import shutil import sys @@ -140,50 +139,19 @@ def get_diff_string(self, branch_ref, file_path): theme_specific_commit, base_branch_commit = list( self.repo.iter_commits(branch_ref, max_count=2) ) - diff_index = base_branch_commit.diff(theme_specific_commit) + diff_index = base_branch_commit.diff( + theme_specific_commit, + paths=file_path, + create_patch=True, + unified=0, + output_indicator_new="", + ) for diff_item in diff_index.iter_change_type("M"): - if diff_item.a_path == diff_item.b_path and diff_item.a_path == file_path: - - base_blob = ( - diff_item.a_blob.data_stream.read() - .decode("utf-8") - .splitlines(keepends=True) - ) - theme_blob = ( - diff_item.b_blob.data_stream.read() - .decode("utf-8") - .splitlines(keepends=True) - ) - diff = ndiff(base_blob, theme_blob) - return self.diff_to_string(diff) + diff_lines = diff_item.diff.decode("utf-8").splitlines(keepends=True) + added_lines = filter(lambda line: not line.startswith("@@ "), diff_lines) + return "".join(added_lines) return "" - @staticmethod - def diff_to_string(diff): - """ - Converts a diff generated by ndiff to a string, - only consisting of the different lines. - - Parameters - ---------- - diff : Differ-style delta - diff generated by ndiff - - Returns - ------- - str - String containing only the different lines. - """ - diff_list = [ - diff_line.replace("+ ", "", 1) - for diff_line in diff - if diff_line.startswith("+ ") - ] - if len(diff_list): - return "".join(diff_list) - else: - return "" - def get_theme_requirements(self): """ Reads all the diff of 'doc/requirements.txt' for all theme branches From 2e3404c570ba6e1a55468d1678f73547c04dd60e Mon Sep 17 00:00:00 2001 From: s-weigand Date: Sun, 26 Jan 2020 18:43:25 +0100 Subject: [PATCH 13/13] refactored diffing to not use '--output-indicator-new' since it is git>=2.22 --- dev_utils/theme_builder.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dev_utils/theme_builder.py b/dev_utils/theme_builder.py index 34a1f14b..ab3a5461 100644 --- a/dev_utils/theme_builder.py +++ b/dev_utils/theme_builder.py @@ -144,12 +144,11 @@ def get_diff_string(self, branch_ref, file_path): paths=file_path, create_patch=True, unified=0, - output_indicator_new="", ) for diff_item in diff_index.iter_change_type("M"): diff_lines = diff_item.diff.decode("utf-8").splitlines(keepends=True) - added_lines = filter(lambda line: not line.startswith("@@ "), diff_lines) - return "".join(added_lines) + added_lines = filter(lambda line: line.startswith("+"), diff_lines) + return "".join([added_line.replace("+", "", 1) for added_line in added_lines]) return "" def get_theme_requirements(self):