From 4d9ddd52c4f7a62c39bb9cc009e266b6666bc38b Mon Sep 17 00:00:00 2001 From: tomc271 Date: Tue, 11 Jul 2023 11:02:42 +0100 Subject: [PATCH 01/33] Add script to bump version numbers. (Initial revision). [skip ci] --- .../update_version_number_in_files.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tools/pylib/_boutpp_build/update_version_number_in_files.py diff --git a/tools/pylib/_boutpp_build/update_version_number_in_files.py b/tools/pylib/_boutpp_build/update_version_number_in_files.py new file mode 100644 index 0000000000..9497690a2e --- /dev/null +++ b/tools/pylib/_boutpp_build/update_version_number_in_files.py @@ -0,0 +1,53 @@ +from pathlib import Path +import re + + +def get_full_filepath(filepath): + + return Path(r"C:\git\BOUT-dev") / filepath + + +def update_version_number_in_file(relative_filepath, pattern, new_version_number): + + def get_replacement(match): + return match[0].replace(match[1], new_version_number) + + full_filepath = get_full_filepath(relative_filepath) + with open(full_filepath, "r", encoding='UTF-8') as file: + file_contents = file.read() + updated_text = re.sub(pattern, get_replacement, file_contents, flags=re.MULTILINE) + with open(full_filepath, "w", encoding='UTF-8') as file: + file.write(updated_text) + + +def bump_version_numbers(new_version_number): + + short_version_number = new_version_number[0:3] + new_minor_version_number = str(int(new_version_number[2]) + 1) + bout_next_version_number = re.sub(r"(?<=\d\.)\d(?=\.\d)", new_minor_version_number, new_version_number) + + update_version_number_in_file("configure.ac", + r"^AC_INIT\(\[BOUT\+\+\],\[(\d\.\d\.\d)\]", new_version_number) + update_version_number_in_file("CITATION.cff", + r"^version: (\d\.\d\.\d)", new_version_number) + update_version_number_in_file("manual/sphinx/conf.py", + r"^version = \"(\d\.\d)\"", short_version_number) + update_version_number_in_file("manual/sphinx/conf.py", + r"^release = \"(\d\.\d\.\d)\"", new_version_number) + update_version_number_in_file("manual/doxygen/Doxyfile_readthedocs", + r"^PROJECT_NUMBER = (\d\.\d\.\d)", new_version_number) + update_version_number_in_file("manual/doxygen/Doxyfile", + r"^PROJECT_NUMBER = (\d\.\d\.\d)", new_version_number) + update_version_number_in_file("CMakeLists.txt", + r"^set\(_bout_previous_version \"v(\d\.\d\.\d)\"\)", new_version_number) + update_version_number_in_file("CMakeLists.txt", + r"^set\(_bout_next_version \"(\d\.\d\.\d)\"\)", bout_next_version_number) + update_version_number_in_file("tools/pylib/_boutpp_build/backend.py", + r"_bout_previous_version = \"v(\d\.\d\.\d)\"", new_version_number) + update_version_number_in_file("tools/pylib/_boutpp_build/backend.py", + r"_bout_next_version = \"v(\d\.\d\.\d)\"", bout_next_version_number) + + +if __name__ == '__main__': + + bump_version_numbers(new_version_number="6.1.0") From 988753d66992d07147da29874272d3b05c35dd1a Mon Sep 17 00:00:00 2001 From: tomc271 Date: Tue, 11 Jul 2023 11:03:08 +0100 Subject: [PATCH 02/33] Add script to check for new authors. (Initial revision). [skip ci] --- tools/pylib/_boutpp_build/update_citations.py | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 tools/pylib/_boutpp_build/update_citations.py diff --git a/tools/pylib/_boutpp_build/update_citations.py b/tools/pylib/_boutpp_build/update_citations.py new file mode 100644 index 0000000000..2e276a8c5c --- /dev/null +++ b/tools/pylib/_boutpp_build/update_citations.py @@ -0,0 +1,166 @@ +from pathlib import Path +from unidecode import unidecode +import yaml + +authors_from_git = ["A Allen", "Aaron Fisher", "Adam Dempsey", "Andrew Allen", "arkabokshi", "Ben Dudson", "bendudson", + "Benjamin Dudson", "Brendan", "Brendan Shanahan", "Brett Friedman", "brey", "BS", "bshanahan", + "Chenhao Ma", "Chris MacMackin", "D Dickinson", "David", "David Bold", "David Dickinson", + "David Schwörer", "dependabot[bot]", "Dmitry Feliksovich Meyerson", "Dmitry Meyerson", "dschwoerer", + "Eric Medwedeff", "Erik Grinaker", "Fabio Riva", "George Breyiannis", "GitHub Merge Button", + "github-actions[bot]", "hahahasan", "Haruki Seto", "Haruki SETO", "holger", "Holger Jones", + "Hong Zhang", "Ilon Joseph", "Ilon Joseph - x31405", "Jarrod Leddy", "JB Leddy", "j-b-o", + "Jed Brown", "Jens Madsen", "John Omotani", "johnomotani", "jonesholger", "Joseph Parker", + "Joshua Sauppe", "Kab Seok Kang", "kangkabseok", "Kevin Savage", "Licheng Wang", "loeiten", + "Luke Easy", "M Leconte", "M V Umansky", "Marta Estarellas", "Matt Thomas", "Maxim Umansky", + "Maxim Umansky - x26041", "Michael Løiten", "Michael Loiten Magnussen", "Minwoo Kim", + "Nicholas Walkden", "Nick Walkden", "nick-walkden", "Olivier Izacard", "Pengwei Xi", "Peter Hill", + "Peter Naylor", "Qin, Yining", "Sajidah Ahmed", "Sanat Tiwari", "Sean Farley", "seanfarley", + "Seto Haruki", "Simon Myers", "Tianyang Xia", "Toby James", "tomc271", "Tongnyeol Rhee", + "Xiang Liu", "Xinliang Xu", "Xueqiao Xu", "Yining Qin", "ZedThree", "Zhanhui Wang"] + + +def parse_cff_file(filename): + with open(filename, "r", encoding='UTF-8') as stream: + try: + return yaml.safe_load(stream) + except yaml.YAMLError as exc: + print(exc) + + +def get_authors_from_cff_file(): + filename = Path(r"C:\git\BOUT-dev") / "CITATION.cff" + file_contents = parse_cff_file(filename) + try: + return file_contents["authors"] + except KeyError as key_error: + print("Failed to find section:", key_error, "in", filename) + + +class ExistingAuthorNames: + + def __init__(self, existing_authors): + self._existing_author_names = \ + [(unidecode(a.get("given-names")), unidecode(a.get("family-names"))) for a in existing_authors] + + def last_name_matches_surname_and_first_name_or_first_letter_matches_given_name(self, last_name, first_name): + matches = [n for n in self._existing_author_names if + n[1].casefold() == last_name.casefold()] # Last name matches surname + + for match in matches: + if match[0].casefold() == first_name.casefold(): # The given name also matches author first name + return True + if match[0][0].casefold() == first_name[0].casefold(): # The first initial matches author first name + return True + + def first_name_matches_surname_and_last_name_matches_given_name(self, first_name, last_name): + matches = [n for n in self._existing_author_names if + n[1].casefold() == first_name.casefold()] # First name matches surname + + for match in matches: + if match[0].casefold() == last_name.casefold(): # The given name also matches author last name + return True + + def surname_matches_whole_author_name(self, author): + + surname_matches = [n for n in self._existing_author_names if n[1].casefold() == author.casefold()] + if len(surname_matches) > 0: + return True + + def given_name_matches_matches_whole_author_name(self, author): + given_name_matches = [n for n in self._existing_author_names if n[0].casefold() == author.casefold()] + if len(given_name_matches) > 0: + return True + + def combined_name_matches_whole_author_name(self, author): + + combined_name_matches = [n for n in self._existing_author_names if + (n[0] + n[1]).casefold() == author.casefold()] + if len(combined_name_matches) > 0: + return True + + def combined_name_reversed_matches(self, author): + + combined_name_reversed_matches = [n for n in self._existing_author_names if + (n[1] + n[0]).casefold() == author.casefold()] + if len(combined_name_reversed_matches) > 0: + return True + + def author_name_is_first_initial_and_surname_concatenated(self, author): + first_character = author[0] + remaining_characters = author[1:] + matches = [n for n in self._existing_author_names if + n[1].casefold() == remaining_characters.casefold()] # Second part of name matches surname + for match in matches: + if match[0][0].casefold() == first_character.casefold(): # The first initial matches author first name + return True + + +def author_found_in_existing_authors(author, existing_authors): + existing_author_names = ExistingAuthorNames(existing_authors) + + names = author.split() + first_name = unidecode(names[0].replace(",", "")) + last_name = unidecode(names[-1]) + + if existing_author_names.last_name_matches_surname_and_first_name_or_first_letter_matches_given_name( + last_name, first_name): + return True + + if existing_author_names.first_name_matches_surname_and_last_name_matches_given_name(first_name, last_name): + return True + + if existing_author_names.surname_matches_whole_author_name(author): + return True + + if existing_author_names.given_name_matches_matches_whole_author_name(author): + return True + + if existing_author_names.combined_name_matches_whole_author_name(author): + return True + + if existing_author_names.combined_name_reversed_matches(author): + return True + + if existing_author_names.author_name_is_first_initial_and_surname_concatenated(author): + return True + + return False + + +def update_citations(): + + existing_authors = get_authors_from_cff_file() + + nonhuman_authors = [a for a in authors_from_git if "github" in a.casefold() or "dependabot" in a.casefold()] + + known_authors = [a for a in authors_from_git if a in KNOWN_AUTHORS] + + human_authors = [a for a in authors_from_git if a not in nonhuman_authors] + + authors_to_search_for = [a for a in human_authors if a not in known_authors] + + unrecognised_authors = [a for a in authors_to_search_for if + not author_found_in_existing_authors(a, existing_authors)] + + print("The following authors were not recognised. Add to citations?") + for author in unrecognised_authors: + print(author) + + +KNOWN_AUTHORS = {"bendudson", + "brey", + "David Schwörer", + "dschwoerer", + "hahahasan", + "Ilon Joseph - x31405", + "kangkabseok", + "loeiten", + "Michael Loiten Magnussen", + "Maxim Umansky - x26041", + "nick-walkden", + "ZedThree", + # "tomc271" + } + +if __name__ == '__main__': + update_citations() From 88f709efc0722043840b8cde3a15da7bb93d2994 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Wed, 12 Jul 2023 10:02:00 +0100 Subject: [PATCH 03/33] Move scripts to bin directory --- {tools/pylib/_boutpp_build => bin}/update_citations.py | 0 .../pylib/_boutpp_build => bin}/update_version_number_in_files.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {tools/pylib/_boutpp_build => bin}/update_citations.py (100%) rename {tools/pylib/_boutpp_build => bin}/update_version_number_in_files.py (100%) diff --git a/tools/pylib/_boutpp_build/update_citations.py b/bin/update_citations.py similarity index 100% rename from tools/pylib/_boutpp_build/update_citations.py rename to bin/update_citations.py diff --git a/tools/pylib/_boutpp_build/update_version_number_in_files.py b/bin/update_version_number_in_files.py similarity index 100% rename from tools/pylib/_boutpp_build/update_version_number_in_files.py rename to bin/update_version_number_in_files.py From 80d5a4f0263b78c9253c5d89b45b51956a18499e Mon Sep 17 00:00:00 2001 From: tomc271 Date: Wed, 12 Jul 2023 10:24:45 +0100 Subject: [PATCH 04/33] Remove hard-coded paths. --- bin/update_citations.py | 6 ++++-- bin/update_version_number_in_files.py | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index 2e276a8c5c..a9936aaed9 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -1,6 +1,7 @@ from pathlib import Path -from unidecode import unidecode +import os import yaml +from unidecode import unidecode authors_from_git = ["A Allen", "Aaron Fisher", "Adam Dempsey", "Andrew Allen", "arkabokshi", "Ben Dudson", "bendudson", "Benjamin Dudson", "Brendan", "Brendan Shanahan", "Brett Friedman", "brey", "BS", "bshanahan", @@ -28,7 +29,8 @@ def parse_cff_file(filename): def get_authors_from_cff_file(): - filename = Path(r"C:\git\BOUT-dev") / "CITATION.cff" + main_directory = Path(os.path.abspath(__file__)).parent.parent + filename = Path(main_directory) / "CITATION.cff" file_contents = parse_cff_file(filename) try: return file_contents["authors"] diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 9497690a2e..261e21108d 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -1,10 +1,12 @@ from pathlib import Path +import os import re def get_full_filepath(filepath): - return Path(r"C:\git\BOUT-dev") / filepath + main_directory = Path(os.path.abspath(__file__)).parent.parent + return Path(main_directory) / filepath def update_version_number_in_file(relative_filepath, pattern, new_version_number): From 7df3104af43e3e3398dc60762215368fcf6e3106 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Wed, 12 Jul 2023 10:28:04 +0100 Subject: [PATCH 05/33] Make KNOWN_AUTHORS a dictionary, holding more information to help identify the known authors. --- bin/update_citations.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index a9936aaed9..0c7c606660 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -2,6 +2,7 @@ import os import yaml from unidecode import unidecode +from typing import NamedTuple authors_from_git = ["A Allen", "Aaron Fisher", "Adam Dempsey", "Andrew Allen", "arkabokshi", "Ben Dudson", "bendudson", "Benjamin Dudson", "Brendan", "Brendan Shanahan", "Brett Friedman", "brey", "BS", "bshanahan", @@ -149,19 +150,24 @@ def update_citations(): print(author) -KNOWN_AUTHORS = {"bendudson", - "brey", - "David Schwörer", - "dschwoerer", - "hahahasan", - "Ilon Joseph - x31405", - "kangkabseok", - "loeiten", - "Michael Loiten Magnussen", - "Maxim Umansky - x26041", - "nick-walkden", - "ZedThree", - # "tomc271" +class KnownAuthor(NamedTuple): + family_names: str + given_names: str + + +KNOWN_AUTHORS = {"bendudson": KnownAuthor("Dodson", "Benjamin"), + "brey": KnownAuthor("Breyiannis", "George"), + "David Schwörer": KnownAuthor("Bold", "David"), + "dschwoerer": KnownAuthor("Bold", "David"), + "hahahasan": KnownAuthor("Muhammed", "Hasan"), + "Ilon Joseph - x31405": KnownAuthor("Joseph", "Ilon"), + "kangkabseok": KnownAuthor("Kang", "Kab Seok"), + "loeiten": KnownAuthor("Løiten", "Michael"), + "Michael Loiten Magnussen": KnownAuthor("Løiten", "Michael"), + "Maxim Umansky - x26041": KnownAuthor("Umansky", "Maxim"), + "nick-walkden": KnownAuthor("Walkden", "Nicholas"), + "ZedThree": KnownAuthor("Hill", "Peter") + # "tomc271": KnownAuthor("Chapman", "Tom") } if __name__ == '__main__': From 80129ae2395cf0a7c68585054e7194a4a30e5da9 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Wed, 12 Jul 2023 10:33:58 +0100 Subject: [PATCH 06/33] Handle version numbers with multiple digits. --- bin/update_version_number_in_files.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 261e21108d..8234fc9f7c 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -26,28 +26,28 @@ def bump_version_numbers(new_version_number): short_version_number = new_version_number[0:3] new_minor_version_number = str(int(new_version_number[2]) + 1) - bout_next_version_number = re.sub(r"(?<=\d\.)\d(?=\.\d)", new_minor_version_number, new_version_number) + bout_next_version_number = re.sub(r"(?<=\d\.)\d+(?=\.\d+)", new_minor_version_number, new_version_number) update_version_number_in_file("configure.ac", - r"^AC_INIT\(\[BOUT\+\+\],\[(\d\.\d\.\d)\]", new_version_number) + r"^AC_INIT\(\[BOUT\+\+\],\[(\d+\.\d+\.\d+)\]", new_version_number) update_version_number_in_file("CITATION.cff", - r"^version: (\d\.\d\.\d)", new_version_number) + r"^version: (\d+\.\d+\.\d+)", new_version_number) update_version_number_in_file("manual/sphinx/conf.py", - r"^version = \"(\d\.\d)\"", short_version_number) + r"^version = \"(\d+\.\d+)\"", short_version_number) update_version_number_in_file("manual/sphinx/conf.py", - r"^release = \"(\d\.\d\.\d)\"", new_version_number) + r"^release = \"(\d+\.\d+\.\d+)\"", new_version_number) update_version_number_in_file("manual/doxygen/Doxyfile_readthedocs", - r"^PROJECT_NUMBER = (\d\.\d\.\d)", new_version_number) + r"^PROJECT_NUMBER = (\d+\.\d+\.\d+)", new_version_number) update_version_number_in_file("manual/doxygen/Doxyfile", - r"^PROJECT_NUMBER = (\d\.\d\.\d)", new_version_number) + r"^PROJECT_NUMBER = (\d+\.\d+\.\d+)", new_version_number) update_version_number_in_file("CMakeLists.txt", - r"^set\(_bout_previous_version \"v(\d\.\d\.\d)\"\)", new_version_number) + r"^set\(_bout_previous_version \"v(\d+\.\d+\.\d+)\"\)", new_version_number) update_version_number_in_file("CMakeLists.txt", - r"^set\(_bout_next_version \"(\d\.\d\.\d)\"\)", bout_next_version_number) + r"^set\(_bout_next_version \"(\d+\.\d+\.\d+)\"\)", bout_next_version_number) update_version_number_in_file("tools/pylib/_boutpp_build/backend.py", - r"_bout_previous_version = \"v(\d\.\d\.\d)\"", new_version_number) + r"_bout_previous_version = \"v(\d+\.\d+\.\d+)\"", new_version_number) update_version_number_in_file("tools/pylib/_boutpp_build/backend.py", - r"_bout_next_version = \"v(\d\.\d\.\d)\"", bout_next_version_number) + r"_bout_next_version = \"v(\d+\.\d+\.\d+)\"", bout_next_version_number) if __name__ == '__main__': From 0111768c0a08ce9fabbc32ff62af3354ca235ca4 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Wed, 12 Jul 2023 11:12:05 +0100 Subject: [PATCH 07/33] Add classes VersionNumber and ShortVersionNumber. --- bin/update_version_number_in_files.py | 37 +++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 8234fc9f7c..24ba8bd7b6 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -12,7 +12,7 @@ def get_full_filepath(filepath): def update_version_number_in_file(relative_filepath, pattern, new_version_number): def get_replacement(match): - return match[0].replace(match[1], new_version_number) + return match[0].replace(match[1], new_version_number.as_string()) full_filepath = get_full_filepath(relative_filepath) with open(full_filepath, "r", encoding='UTF-8') as file: @@ -24,9 +24,10 @@ def get_replacement(match): def bump_version_numbers(new_version_number): - short_version_number = new_version_number[0:3] - new_minor_version_number = str(int(new_version_number[2]) + 1) - bout_next_version_number = re.sub(r"(?<=\d\.)\d+(?=\.\d+)", new_minor_version_number, new_version_number) + short_version_number = ShortVersionNumber(new_version_number.major_version, new_version_number.minor_version) + bout_next_version_number = VersionNumber(new_version_number.major_version, + new_version_number.minor_version + 1, + new_version_number.patch_version) update_version_number_in_file("configure.ac", r"^AC_INIT\(\[BOUT\+\+\],\[(\d+\.\d+\.\d+)\]", new_version_number) @@ -50,6 +51,32 @@ def bump_version_numbers(new_version_number): r"_bout_next_version = \"v(\d+\.\d+\.\d+)\"", bout_next_version_number) +class VersionNumber: + + major_version: int + minor_version: int + patch_version: int + + def __init__(self, major_version, minor_version, patch_version): + self.major_version = major_version + self.minor_version = minor_version + self.patch_version = patch_version + + def as_string(self): + return "%d.%d.%d" % (self.major_version, self.minor_version, self.patch_version) + + +class ShortVersionNumber(VersionNumber): + + def __init__(self, major_version, minor_version): + self.major_version = major_version + self.minor_version = minor_version + self.patch_version = None + + def as_string(self): + return "%d.%d" % (self.major_version, self.minor_version) + + if __name__ == '__main__': - bump_version_numbers(new_version_number="6.1.0") + bump_version_numbers(new_version_number=VersionNumber(63, 15, 12)) From f329b4744af8a88eb261761fc7cff24d937d1b77 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Thu, 13 Jul 2023 17:08:07 +0100 Subject: [PATCH 08/33] Run git log command to get the list of authors. --- bin/update_citations.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index 0c7c606660..a25eae04f9 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -1,24 +1,27 @@ +import subprocess from pathlib import Path import os import yaml from unidecode import unidecode from typing import NamedTuple -authors_from_git = ["A Allen", "Aaron Fisher", "Adam Dempsey", "Andrew Allen", "arkabokshi", "Ben Dudson", "bendudson", - "Benjamin Dudson", "Brendan", "Brendan Shanahan", "Brett Friedman", "brey", "BS", "bshanahan", - "Chenhao Ma", "Chris MacMackin", "D Dickinson", "David", "David Bold", "David Dickinson", - "David Schwörer", "dependabot[bot]", "Dmitry Feliksovich Meyerson", "Dmitry Meyerson", "dschwoerer", - "Eric Medwedeff", "Erik Grinaker", "Fabio Riva", "George Breyiannis", "GitHub Merge Button", - "github-actions[bot]", "hahahasan", "Haruki Seto", "Haruki SETO", "holger", "Holger Jones", - "Hong Zhang", "Ilon Joseph", "Ilon Joseph - x31405", "Jarrod Leddy", "JB Leddy", "j-b-o", - "Jed Brown", "Jens Madsen", "John Omotani", "johnomotani", "jonesholger", "Joseph Parker", - "Joshua Sauppe", "Kab Seok Kang", "kangkabseok", "Kevin Savage", "Licheng Wang", "loeiten", - "Luke Easy", "M Leconte", "M V Umansky", "Marta Estarellas", "Matt Thomas", "Maxim Umansky", - "Maxim Umansky - x26041", "Michael Løiten", "Michael Loiten Magnussen", "Minwoo Kim", - "Nicholas Walkden", "Nick Walkden", "nick-walkden", "Olivier Izacard", "Pengwei Xi", "Peter Hill", - "Peter Naylor", "Qin, Yining", "Sajidah Ahmed", "Sanat Tiwari", "Sean Farley", "seanfarley", - "Seto Haruki", "Simon Myers", "Tianyang Xia", "Toby James", "tomc271", "Tongnyeol Rhee", - "Xiang Liu", "Xinliang Xu", "Xueqiao Xu", "Yining Qin", "ZedThree", "Zhanhui Wang"] + +def get_authors_from_git(): + + main_directory = Path(os.path.abspath(__file__)).parent.parent + subprocess.run(["cd", main_directory], shell=True) + + output = subprocess.run(["git", "log", "--format='%aN'"], capture_output=True) + if output.stderr: + return output.stderr + + authors_string = output.stdout.decode() + authors_list = authors_string.split('\n') + authors_without_quotes = [a.strip("'") for a in authors_list] + + distinct_authors = set(authors_without_quotes) + distinct_authors_list_without_initial_empty_string = list(distinct_authors)[1:] + return distinct_authors_list_without_initial_empty_string def parse_cff_file(filename): @@ -132,8 +135,6 @@ def author_found_in_existing_authors(author, existing_authors): def update_citations(): - existing_authors = get_authors_from_cff_file() - nonhuman_authors = [a for a in authors_from_git if "github" in a.casefold() or "dependabot" in a.casefold()] known_authors = [a for a in authors_from_git if a in KNOWN_AUTHORS] @@ -171,4 +172,7 @@ class KnownAuthor(NamedTuple): } if __name__ == '__main__': + + authors_from_git = get_authors_from_git() + existing_authors = get_authors_from_cff_file() update_citations() From 7ec28f49017ccb90015c98a7189913f0320bd4af Mon Sep 17 00:00:00 2001 From: tomc271 Date: Thu, 13 Jul 2023 17:11:37 +0100 Subject: [PATCH 09/33] Extracted function get_main_directory(). --- bin/update_citations.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index a25eae04f9..c4f5eed142 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -6,9 +6,13 @@ from typing import NamedTuple +def get_main_directory(): + return Path(os.path.abspath(__file__)).parent.parent + + def get_authors_from_git(): - main_directory = Path(os.path.abspath(__file__)).parent.parent + main_directory = get_main_directory() subprocess.run(["cd", main_directory], shell=True) output = subprocess.run(["git", "log", "--format='%aN'"], capture_output=True) From b4db2749caec649d1ed178204746d90eaf7028df Mon Sep 17 00:00:00 2001 From: tomc271 Date: Thu, 13 Jul 2023 17:23:51 +0100 Subject: [PATCH 10/33] Also list emails for unrecognised authors. [skip ci] --- bin/update_citations.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index c4f5eed142..99f3e2c11c 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -15,7 +15,7 @@ def get_authors_from_git(): main_directory = get_main_directory() subprocess.run(["cd", main_directory], shell=True) - output = subprocess.run(["git", "log", "--format='%aN'"], capture_output=True) + output = subprocess.run(["git", "log", "--format='%aN %aE'"], capture_output=True) if output.stderr: return output.stderr @@ -24,8 +24,9 @@ def get_authors_from_git(): authors_without_quotes = [a.strip("'") for a in authors_list] distinct_authors = set(authors_without_quotes) - distinct_authors_list_without_initial_empty_string = list(distinct_authors)[1:] - return distinct_authors_list_without_initial_empty_string + distinct_authors_list_without_empty_strings = [a for a in distinct_authors if a] + authors_with_emails = [a.rsplit(maxsplit=1) for a in distinct_authors_list_without_empty_strings] + return authors_with_emails def parse_cff_file(filename): @@ -106,6 +107,7 @@ def author_name_is_first_initial_and_surname_concatenated(self, author): def author_found_in_existing_authors(author, existing_authors): + existing_author_names = ExistingAuthorNames(existing_authors) names = author.split() @@ -139,16 +141,16 @@ def author_found_in_existing_authors(author, existing_authors): def update_citations(): - nonhuman_authors = [a for a in authors_from_git if "github" in a.casefold() or "dependabot" in a.casefold()] + nonhuman_authors = [a for a in authors_from_git if "github" in a[0].casefold() or "dependabot" in a[0].casefold()] - known_authors = [a for a in authors_from_git if a in KNOWN_AUTHORS] + known_authors = [a for a in authors_from_git if a[0] in KNOWN_AUTHORS] human_authors = [a for a in authors_from_git if a not in nonhuman_authors] authors_to_search_for = [a for a in human_authors if a not in known_authors] unrecognised_authors = [a for a in authors_to_search_for if - not author_found_in_existing_authors(a, existing_authors)] + not author_found_in_existing_authors(a[0], existing_authors)] print("The following authors were not recognised. Add to citations?") for author in unrecognised_authors: From 048207d0363783c03f38998ccc60f9d88c75dfac Mon Sep 17 00:00:00 2001 From: tomc271 Date: Thu, 10 Aug 2023 10:03:46 +0100 Subject: [PATCH 11/33] Copy functions from other scripts: apply_fixes(replacements, source) yes_or_no(question) create_patch(filename, original, modified) [unfinished] --- bin/update_version_number_in_files.py | 130 +++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 24ba8bd7b6..5ef8da844b 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -1,4 +1,8 @@ from pathlib import Path +import argparse +import difflib +import copy +import textwrap import os import re @@ -77,6 +81,130 @@ def as_string(self): return "%d.%d" % (self.major_version, self.minor_version) +def apply_fixes(replacements, source): + + raise NotImplementedError + # """Apply all fixes in this module""" + # modified = copy.deepcopy(source) + # + # for replacement in replacements: + # if replacement["new"] is None: + # print( + # "'%s' has been removed, please delete from your code" + # % replacement["old"] + # ) + # continue + # + # modified = fix_include_version_header( + # replacement["old"], replacement["headers"], modified + # ) + # if replacement["macro"] and replacement["always_defined"]: + # modified = fix_always_defined_macros( + # replacement["old"], replacement["new"], modified + # ) + # elif replacement["always_defined"]: + # modified = fix_ifdefs(replacement["old"], modified) + # modified = fix_replacement(replacement["old"], replacement["new"], modified) + # + # return modified + + +def yes_or_no(question): + """Convert user input from yes/no variations to True/False""" + while True: + reply = input(question + " [y/N] ").lower().strip() + if not reply or reply[0] == "n": + return False + if reply[0] == "y": + return True + + +def create_patch(filename, original, modified): + """Create a unified diff between original and modified""" + + patch = "\n".join( + difflib.unified_diff( + original.splitlines(), + modified.splitlines(), + fromfile=filename, + tofile=filename, + lineterm="", + ) + ) + + return patch + + if __name__ == '__main__': - bump_version_numbers(new_version_number=VersionNumber(63, 15, 12)) + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=textwrap.dedent( + """\ + Fix macro defines for BOUT++ v4 -> v5 + + Please note that this is only slightly better than dumb text replacement. It + will fix the following: + + * replacement of macros with variables or new names + * inclusion of correct headers for new variables + * removal of #if(n)def/#endif blocks that do simple checks for the old + macro, keeping the appriopriate part, if replaced by a variable + * change '#if(n)def' for '#if (!)' if the replacment is always defined + + It will try not to replace quoted macro names, but may + still replace them in strings or comments. + + Please check the diff output carefully! + """ + ), + ) + + parser.add_argument("files", action="store", nargs="+", help="Input files") + parser.add_argument( + "--force", "-f", action="store_true", help="Make changes without asking" + ) + parser.add_argument( + "--quiet", "-q", action="store_true", help="Don't print patches" + ) + parser.add_argument( + "--patch-only", "-p", action="store_true", help="Print the patches and exit" + ) + + args = parser.parse_args() + + if args.force and args.patch_only: + raise ValueError("Incompatible options: --force and --patch") + + for filename in args.files: + with open(filename, "r") as f: + contents = f.read() + original = copy.deepcopy(contents) + + replacements = bump_version_numbers(new_version_number=VersionNumber(63, 15, 12)) + modified = apply_fixes(replacements, contents) + patch = create_patch(filename, original, modified) + + if args.patch_only: + print(patch) + continue + + if not patch: + if not args.quiet: + print("No changes to make to {}".format(filename)) + continue + + if not args.quiet: + print("\n******************************************") + print("Changes to {}\n".format(filename)) + print(patch) + print("\n******************************************") + + if args.force: + make_change = True + else: + make_change = yes_or_no("Make changes to {}?".format(filename)) + + if make_change: + with open(filename, "w") as f: + f.write(modified) From db5d413b0d7e2770eb8ada1723b49ee48178b709 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Thu, 10 Aug 2023 16:14:53 +0100 Subject: [PATCH 12/33] Apply copied functions to this script. --- bin/update_version_number_in_files.py | 108 +++++++++++++------------- 1 file changed, 52 insertions(+), 56 deletions(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 5ef8da844b..7a570000a6 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -15,15 +15,39 @@ def get_full_filepath(filepath): def update_version_number_in_file(relative_filepath, pattern, new_version_number): - def get_replacement(match): - return match[0].replace(match[1], new_version_number.as_string()) - full_filepath = get_full_filepath(relative_filepath) + with open(full_filepath, "r", encoding='UTF-8') as file: + file_contents = file.read() - updated_text = re.sub(pattern, get_replacement, file_contents, flags=re.MULTILINE) - with open(full_filepath, "w", encoding='UTF-8') as file: - file.write(updated_text) + original = copy.deepcopy(file_contents) + + modified = apply_fixes(pattern, new_version_number, file_contents) + patch = create_patch(str(full_filepath), original, modified) + + if args.patch_only: + print(patch) + return + + if not patch: + if not args.quiet: + print("No changes to make to {}".format(full_filepath)) + return + + if not args.quiet: + print("\n******************************************") + print("Changes to {}\n".format(full_filepath)) + print(patch) + print("\n******************************************") + + if args.force: + make_change = True + else: + make_change = yes_or_no("Make changes to {}?".format(full_filepath)) + + if make_change: + with open(full_filepath, "w", encoding='UTF-8') as file: + file.write(modified) def bump_version_numbers(new_version_number): @@ -81,9 +105,27 @@ def as_string(self): return "%d.%d" % (self.major_version, self.minor_version) -def apply_fixes(replacements, source): +def apply_fixes(pattern, new_version_number, source): + """Apply the various fixes for each factory to source. Returns + modified source + + Parameters + ---------- + pattern + Regex pattern to apply for replacement + new_version_number + New version number to use in replacement + source + Text to update + """ + + def get_replacement(match): + return match[0].replace(match[1], new_version_number.as_string()) + + modified = re.sub(pattern, get_replacement, source, flags=re.MULTILINE) + + return modified - raise NotImplementedError # """Apply all fixes in this module""" # modified = copy.deepcopy(source) # @@ -141,26 +183,11 @@ def create_patch(filename, original, modified): formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent( """\ - Fix macro defines for BOUT++ v4 -> v5 - - Please note that this is only slightly better than dumb text replacement. It - will fix the following: - - * replacement of macros with variables or new names - * inclusion of correct headers for new variables - * removal of #if(n)def/#endif blocks that do simple checks for the old - macro, keeping the appriopriate part, if replaced by a variable - * change '#if(n)def' for '#if (!)' if the replacment is always defined - - It will try not to replace quoted macro names, but may - still replace them in strings or comments. - - Please check the diff output carefully! + TODO: Description here... """ ), ) - parser.add_argument("files", action="store", nargs="+", help="Input files") parser.add_argument( "--force", "-f", action="store_true", help="Make changes without asking" ) @@ -176,35 +203,4 @@ def create_patch(filename, original, modified): if args.force and args.patch_only: raise ValueError("Incompatible options: --force and --patch") - for filename in args.files: - with open(filename, "r") as f: - contents = f.read() - original = copy.deepcopy(contents) - - replacements = bump_version_numbers(new_version_number=VersionNumber(63, 15, 12)) - modified = apply_fixes(replacements, contents) - patch = create_patch(filename, original, modified) - - if args.patch_only: - print(patch) - continue - - if not patch: - if not args.quiet: - print("No changes to make to {}".format(filename)) - continue - - if not args.quiet: - print("\n******************************************") - print("Changes to {}\n".format(filename)) - print(patch) - print("\n******************************************") - - if args.force: - make_change = True - else: - make_change = yes_or_no("Make changes to {}?".format(filename)) - - if make_change: - with open(filename, "w") as f: - f.write(modified) + bump_version_numbers(new_version_number=VersionNumber(63, 15, 12)) \ No newline at end of file From 696da0643886f69212931c72acc94a6ee7cce9ad Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 13:10:36 +0100 Subject: [PATCH 13/33] Black formatting [skip ci] --- bin/update_citations.py | 152 +++++++++++++++++--------- bin/update_version_number_in_files.py | 90 +++++++++------ 2 files changed, 154 insertions(+), 88 deletions(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index 99f3e2c11c..232a1fb48c 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -11,7 +11,6 @@ def get_main_directory(): def get_authors_from_git(): - main_directory = get_main_directory() subprocess.run(["cd", main_directory], shell=True) @@ -20,17 +19,19 @@ def get_authors_from_git(): return output.stderr authors_string = output.stdout.decode() - authors_list = authors_string.split('\n') + authors_list = authors_string.split("\n") authors_without_quotes = [a.strip("'") for a in authors_list] distinct_authors = set(authors_without_quotes) distinct_authors_list_without_empty_strings = [a for a in distinct_authors if a] - authors_with_emails = [a.rsplit(maxsplit=1) for a in distinct_authors_list_without_empty_strings] + authors_with_emails = [ + a.rsplit(maxsplit=1) for a in distinct_authors_list_without_empty_strings + ] return authors_with_emails def parse_cff_file(filename): - with open(filename, "r", encoding='UTF-8') as stream: + with open(filename, "r", encoding="UTF-8") as stream: try: return yaml.safe_load(stream) except yaml.YAMLError as exc: @@ -48,66 +49,98 @@ def get_authors_from_cff_file(): class ExistingAuthorNames: - def __init__(self, existing_authors): - self._existing_author_names = \ - [(unidecode(a.get("given-names")), unidecode(a.get("family-names"))) for a in existing_authors] - - def last_name_matches_surname_and_first_name_or_first_letter_matches_given_name(self, last_name, first_name): - matches = [n for n in self._existing_author_names if - n[1].casefold() == last_name.casefold()] # Last name matches surname + self._existing_author_names = [ + (unidecode(a.get("given-names")), unidecode(a.get("family-names"))) + for a in existing_authors + ] + + def last_name_matches_surname_and_first_name_or_first_letter_matches_given_name( + self, last_name, first_name + ): + matches = [ + n + for n in self._existing_author_names + if n[1].casefold() == last_name.casefold() + ] # Last name matches surname for match in matches: - if match[0].casefold() == first_name.casefold(): # The given name also matches author first name + if ( + match[0].casefold() == first_name.casefold() + ): # The given name also matches author first name return True - if match[0][0].casefold() == first_name[0].casefold(): # The first initial matches author first name + if ( + match[0][0].casefold() == first_name[0].casefold() + ): # The first initial matches author first name return True - def first_name_matches_surname_and_last_name_matches_given_name(self, first_name, last_name): - matches = [n for n in self._existing_author_names if - n[1].casefold() == first_name.casefold()] # First name matches surname + def first_name_matches_surname_and_last_name_matches_given_name( + self, first_name, last_name + ): + matches = [ + n + for n in self._existing_author_names + if n[1].casefold() == first_name.casefold() + ] # First name matches surname for match in matches: - if match[0].casefold() == last_name.casefold(): # The given name also matches author last name + if ( + match[0].casefold() == last_name.casefold() + ): # The given name also matches author last name return True def surname_matches_whole_author_name(self, author): - - surname_matches = [n for n in self._existing_author_names if n[1].casefold() == author.casefold()] + surname_matches = [ + n + for n in self._existing_author_names + if n[1].casefold() == author.casefold() + ] if len(surname_matches) > 0: return True def given_name_matches_matches_whole_author_name(self, author): - given_name_matches = [n for n in self._existing_author_names if n[0].casefold() == author.casefold()] + given_name_matches = [ + n + for n in self._existing_author_names + if n[0].casefold() == author.casefold() + ] if len(given_name_matches) > 0: return True def combined_name_matches_whole_author_name(self, author): - - combined_name_matches = [n for n in self._existing_author_names if - (n[0] + n[1]).casefold() == author.casefold()] + combined_name_matches = [ + n + for n in self._existing_author_names + if (n[0] + n[1]).casefold() == author.casefold() + ] if len(combined_name_matches) > 0: return True def combined_name_reversed_matches(self, author): - - combined_name_reversed_matches = [n for n in self._existing_author_names if - (n[1] + n[0]).casefold() == author.casefold()] + combined_name_reversed_matches = [ + n + for n in self._existing_author_names + if (n[1] + n[0]).casefold() == author.casefold() + ] if len(combined_name_reversed_matches) > 0: return True def author_name_is_first_initial_and_surname_concatenated(self, author): first_character = author[0] remaining_characters = author[1:] - matches = [n for n in self._existing_author_names if - n[1].casefold() == remaining_characters.casefold()] # Second part of name matches surname + matches = [ + n + for n in self._existing_author_names + if n[1].casefold() == remaining_characters.casefold() + ] # Second part of name matches surname for match in matches: - if match[0][0].casefold() == first_character.casefold(): # The first initial matches author first name + if ( + match[0][0].casefold() == first_character.casefold() + ): # The first initial matches author first name return True def author_found_in_existing_authors(author, existing_authors): - existing_author_names = ExistingAuthorNames(existing_authors) names = author.split() @@ -115,10 +148,13 @@ def author_found_in_existing_authors(author, existing_authors): last_name = unidecode(names[-1]) if existing_author_names.last_name_matches_surname_and_first_name_or_first_letter_matches_given_name( - last_name, first_name): + last_name, first_name + ): return True - if existing_author_names.first_name_matches_surname_and_last_name_matches_given_name(first_name, last_name): + if existing_author_names.first_name_matches_surname_and_last_name_matches_given_name( + first_name, last_name + ): return True if existing_author_names.surname_matches_whole_author_name(author): @@ -133,15 +169,20 @@ def author_found_in_existing_authors(author, existing_authors): if existing_author_names.combined_name_reversed_matches(author): return True - if existing_author_names.author_name_is_first_initial_and_surname_concatenated(author): + if existing_author_names.author_name_is_first_initial_and_surname_concatenated( + author + ): return True return False def update_citations(): - - nonhuman_authors = [a for a in authors_from_git if "github" in a[0].casefold() or "dependabot" in a[0].casefold()] + nonhuman_authors = [ + a + for a in authors_from_git + if "github" in a[0].casefold() or "dependabot" in a[0].casefold() + ] known_authors = [a for a in authors_from_git if a[0] in KNOWN_AUTHORS] @@ -149,8 +190,11 @@ def update_citations(): authors_to_search_for = [a for a in human_authors if a not in known_authors] - unrecognised_authors = [a for a in authors_to_search_for if - not author_found_in_existing_authors(a[0], existing_authors)] + unrecognised_authors = [ + a + for a in authors_to_search_for + if not author_found_in_existing_authors(a[0], existing_authors) + ] print("The following authors were not recognised. Add to citations?") for author in unrecognised_authors: @@ -162,23 +206,23 @@ class KnownAuthor(NamedTuple): given_names: str -KNOWN_AUTHORS = {"bendudson": KnownAuthor("Dodson", "Benjamin"), - "brey": KnownAuthor("Breyiannis", "George"), - "David Schwörer": KnownAuthor("Bold", "David"), - "dschwoerer": KnownAuthor("Bold", "David"), - "hahahasan": KnownAuthor("Muhammed", "Hasan"), - "Ilon Joseph - x31405": KnownAuthor("Joseph", "Ilon"), - "kangkabseok": KnownAuthor("Kang", "Kab Seok"), - "loeiten": KnownAuthor("Løiten", "Michael"), - "Michael Loiten Magnussen": KnownAuthor("Løiten", "Michael"), - "Maxim Umansky - x26041": KnownAuthor("Umansky", "Maxim"), - "nick-walkden": KnownAuthor("Walkden", "Nicholas"), - "ZedThree": KnownAuthor("Hill", "Peter") - # "tomc271": KnownAuthor("Chapman", "Tom") - } - -if __name__ == '__main__': - +KNOWN_AUTHORS = { + "bendudson": KnownAuthor("Dodson", "Benjamin"), + "brey": KnownAuthor("Breyiannis", "George"), + "David Schwörer": KnownAuthor("Bold", "David"), + "dschwoerer": KnownAuthor("Bold", "David"), + "hahahasan": KnownAuthor("Muhammed", "Hasan"), + "Ilon Joseph - x31405": KnownAuthor("Joseph", "Ilon"), + "kangkabseok": KnownAuthor("Kang", "Kab Seok"), + "loeiten": KnownAuthor("Løiten", "Michael"), + "Michael Loiten Magnussen": KnownAuthor("Løiten", "Michael"), + "Maxim Umansky - x26041": KnownAuthor("Umansky", "Maxim"), + "nick-walkden": KnownAuthor("Walkden", "Nicholas"), + "ZedThree": KnownAuthor("Hill", "Peter") + # "tomc271": KnownAuthor("Chapman", "Tom") +} + +if __name__ == "__main__": authors_from_git = get_authors_from_git() existing_authors = get_authors_from_cff_file() update_citations() diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 7a570000a6..a7e92884d5 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -8,17 +8,14 @@ def get_full_filepath(filepath): - main_directory = Path(os.path.abspath(__file__)).parent.parent return Path(main_directory) / filepath def update_version_number_in_file(relative_filepath, pattern, new_version_number): - full_filepath = get_full_filepath(relative_filepath) - with open(full_filepath, "r", encoding='UTF-8') as file: - + with open(full_filepath, "r", encoding="UTF-8") as file: file_contents = file.read() original = copy.deepcopy(file_contents) @@ -46,41 +43,67 @@ def update_version_number_in_file(relative_filepath, pattern, new_version_number make_change = yes_or_no("Make changes to {}?".format(full_filepath)) if make_change: - with open(full_filepath, "w", encoding='UTF-8') as file: + with open(full_filepath, "w", encoding="UTF-8") as file: file.write(modified) def bump_version_numbers(new_version_number): + short_version_number = ShortVersionNumber( + new_version_number.major_version, new_version_number.minor_version + ) + bout_next_version_number = VersionNumber( + new_version_number.major_version, + new_version_number.minor_version + 1, + new_version_number.patch_version, + ) - short_version_number = ShortVersionNumber(new_version_number.major_version, new_version_number.minor_version) - bout_next_version_number = VersionNumber(new_version_number.major_version, - new_version_number.minor_version + 1, - new_version_number.patch_version) - - update_version_number_in_file("configure.ac", - r"^AC_INIT\(\[BOUT\+\+\],\[(\d+\.\d+\.\d+)\]", new_version_number) - update_version_number_in_file("CITATION.cff", - r"^version: (\d+\.\d+\.\d+)", new_version_number) - update_version_number_in_file("manual/sphinx/conf.py", - r"^version = \"(\d+\.\d+)\"", short_version_number) - update_version_number_in_file("manual/sphinx/conf.py", - r"^release = \"(\d+\.\d+\.\d+)\"", new_version_number) - update_version_number_in_file("manual/doxygen/Doxyfile_readthedocs", - r"^PROJECT_NUMBER = (\d+\.\d+\.\d+)", new_version_number) - update_version_number_in_file("manual/doxygen/Doxyfile", - r"^PROJECT_NUMBER = (\d+\.\d+\.\d+)", new_version_number) - update_version_number_in_file("CMakeLists.txt", - r"^set\(_bout_previous_version \"v(\d+\.\d+\.\d+)\"\)", new_version_number) - update_version_number_in_file("CMakeLists.txt", - r"^set\(_bout_next_version \"(\d+\.\d+\.\d+)\"\)", bout_next_version_number) - update_version_number_in_file("tools/pylib/_boutpp_build/backend.py", - r"_bout_previous_version = \"v(\d+\.\d+\.\d+)\"", new_version_number) - update_version_number_in_file("tools/pylib/_boutpp_build/backend.py", - r"_bout_next_version = \"v(\d+\.\d+\.\d+)\"", bout_next_version_number) + update_version_number_in_file( + "configure.ac", + r"^AC_INIT\(\[BOUT\+\+\],\[(\d+\.\d+\.\d+)\]", + new_version_number, + ) + update_version_number_in_file( + "CITATION.cff", r"^version: (\d+\.\d+\.\d+)", new_version_number + ) + update_version_number_in_file( + "manual/sphinx/conf.py", r"^version = \"(\d+\.\d+)\"", short_version_number + ) + update_version_number_in_file( + "manual/sphinx/conf.py", r"^release = \"(\d+\.\d+\.\d+)\"", new_version_number + ) + update_version_number_in_file( + "manual/doxygen/Doxyfile_readthedocs", + r"^PROJECT_NUMBER = (\d+\.\d+\.\d+)", + new_version_number, + ) + update_version_number_in_file( + "manual/doxygen/Doxyfile", + r"^PROJECT_NUMBER = (\d+\.\d+\.\d+)", + new_version_number, + ) + update_version_number_in_file( + "CMakeLists.txt", + r"^set\(_bout_previous_version \"v(\d+\.\d+\.\d+)\"\)", + new_version_number, + ) + update_version_number_in_file( + "CMakeLists.txt", + r"^set\(_bout_next_version \"(\d+\.\d+\.\d+)\"\)", + bout_next_version_number, + ) + update_version_number_in_file( + "tools/pylib/_boutpp_build/backend.py", + r"_bout_previous_version = \"v(\d+\.\d+\.\d+)\"", + new_version_number, + ) + update_version_number_in_file( + "tools/pylib/_boutpp_build/backend.py", + r"_bout_next_version = \"v(\d+\.\d+\.\d+)\"", + bout_next_version_number, + ) class VersionNumber: - major_version: int minor_version: int patch_version: int @@ -177,8 +200,7 @@ def create_patch(filename, original, modified): return patch -if __name__ == '__main__': - +if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent( @@ -203,4 +225,4 @@ def create_patch(filename, original, modified): if args.force and args.patch_only: raise ValueError("Incompatible options: --force and --patch") - bump_version_numbers(new_version_number=VersionNumber(63, 15, 12)) \ No newline at end of file + bump_version_numbers(new_version_number=VersionNumber(63, 15, 12)) From 9b8bc249c7f7f1571ea051bf4d32866741bc04ce Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 13:11:43 +0100 Subject: [PATCH 14/33] No need for ShortVersionNumber to inherit from VersionNumber --- bin/update_version_number_in_files.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index a7e92884d5..4309e42955 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -117,12 +117,10 @@ def as_string(self): return "%d.%d.%d" % (self.major_version, self.minor_version, self.patch_version) -class ShortVersionNumber(VersionNumber): - +class ShortVersionNumber: def __init__(self, major_version, minor_version): self.major_version = major_version self.minor_version = minor_version - self.patch_version = None def as_string(self): return "%d.%d" % (self.major_version, self.minor_version) From 2d514f02165c139582c06d490c927ae711e39480 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 17:38:55 +0100 Subject: [PATCH 15/33] Use cwd argument to subprocess.run() to change directory (subprocess.run("cd",... does nothing as it's a separate process). [skip ci] --- bin/update_citations.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index 232a1fb48c..993a617a04 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -12,9 +12,9 @@ def get_main_directory(): def get_authors_from_git(): main_directory = get_main_directory() - subprocess.run(["cd", main_directory], shell=True) - - output = subprocess.run(["git", "log", "--format='%aN %aE'"], capture_output=True) + output = subprocess.run( + ["git", "log", "--format='%aN %aE'"], capture_output=True, cwd=main_directory + ) if output.stderr: return output.stderr From 5329cf4c45a2f329eb3f07068fd090b22ec2bb87 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 17:40:26 +0100 Subject: [PATCH 16/33] Use subprocess check=True argument to check for error. [skip ci] --- bin/update_citations.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index 993a617a04..49e56c012e 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -13,10 +13,8 @@ def get_main_directory(): def get_authors_from_git(): main_directory = get_main_directory() output = subprocess.run( - ["git", "log", "--format='%aN %aE'"], capture_output=True, cwd=main_directory + ["git", "log", "--format='%aN %aE'"], capture_output=True, cwd=main_directory, check=True ) - if output.stderr: - return output.stderr authors_string = output.stdout.decode() authors_list = authors_string.split("\n") From c7051f2da0584a2b22f1326d8e5ed48532273efb Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 17:42:24 +0100 Subject: [PATCH 17/33] Use subprocess text=True argument (then no need to decode() stdout). [skip ci] --- bin/update_citations.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index 49e56c012e..63403d5128 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -13,10 +13,14 @@ def get_main_directory(): def get_authors_from_git(): main_directory = get_main_directory() output = subprocess.run( - ["git", "log", "--format='%aN %aE'"], capture_output=True, cwd=main_directory, check=True + ["git", "log", "--format='%aN %aE'"], + capture_output=True, + cwd=main_directory, + check=True, + text=True, ) - authors_string = output.stdout.decode() + authors_string = output.stdout authors_list = authors_string.split("\n") authors_without_quotes = [a.strip("'") for a in authors_list] @@ -31,7 +35,7 @@ def get_authors_from_git(): def parse_cff_file(filename): with open(filename, "r", encoding="UTF-8") as stream: try: - return yaml.safe_load(stream) + return yaml.safe_load(stream) except yaml.YAMLError as exc: print(exc) From 8a27bfa51f074aef94b000c21de80c69daa0f376 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 17:48:59 +0100 Subject: [PATCH 18/33] No need to catch real errors. [skip ci] --- bin/update_citations.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index 63403d5128..9c6464ad96 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -34,10 +34,7 @@ def get_authors_from_git(): def parse_cff_file(filename): with open(filename, "r", encoding="UTF-8") as stream: - try: return yaml.safe_load(stream) - except yaml.YAMLError as exc: - print(exc) def get_authors_from_cff_file(): From f85ad2a92a42193dc88c66bf12cb10b4f3794753 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 17:56:51 +0100 Subject: [PATCH 19/33] When catching KeyError, raise a new exception instead. Use an f-string. [skip ci] --- bin/update_citations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index 9c6464ad96..470a23e3a5 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -44,7 +44,7 @@ def get_authors_from_cff_file(): try: return file_contents["authors"] except KeyError as key_error: - print("Failed to find section:", key_error, "in", filename) + raise ValueError(f"Failed to find section:{key_error} in {filename}") class ExistingAuthorNames: From 19357aa07a7caae86ef5efe073858435c5256e0d Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 18:01:41 +0100 Subject: [PATCH 20/33] Use get_main_directory() function. [skip ci] --- bin/update_citations.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index 470a23e3a5..a672eac343 100644 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -38,8 +38,7 @@ def parse_cff_file(filename): def get_authors_from_cff_file(): - main_directory = Path(os.path.abspath(__file__)).parent.parent - filename = Path(main_directory) / "CITATION.cff" + filename = get_main_directory() / "CITATION.cff" file_contents = parse_cff_file(filename) try: return file_contents["authors"] From 80b914c596c3fccd9caf554ee3c66444dda88c1a Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 18:04:29 +0100 Subject: [PATCH 21/33] Make VersionNumber a dataclass. [skip ci] --- bin/update_version_number_in_files.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 4309e42955..578e75d089 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass from pathlib import Path import argparse import difflib @@ -103,16 +104,12 @@ def bump_version_numbers(new_version_number): ) +@dataclass class VersionNumber: major_version: int minor_version: int patch_version: int - def __init__(self, major_version, minor_version, patch_version): - self.major_version = major_version - self.minor_version = minor_version - self.patch_version = patch_version - def as_string(self): return "%d.%d.%d" % (self.major_version, self.minor_version, self.patch_version) From cbb9823f49442bba8eec33377f9424abf85fb138 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 18:11:18 +0100 Subject: [PATCH 22/33] Use str dunder method. [skip ci] --- bin/update_version_number_in_files.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 578e75d089..2820f82d92 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -110,7 +110,7 @@ class VersionNumber: minor_version: int patch_version: int - def as_string(self): + def __str__(self): return "%d.%d.%d" % (self.major_version, self.minor_version, self.patch_version) @@ -119,7 +119,7 @@ def __init__(self, major_version, minor_version): self.major_version = major_version self.minor_version = minor_version - def as_string(self): + def __str__(self): return "%d.%d" % (self.major_version, self.minor_version) @@ -138,7 +138,7 @@ def apply_fixes(pattern, new_version_number, source): """ def get_replacement(match): - return match[0].replace(match[1], new_version_number.as_string()) + return match[0].replace(match[1], str(new_version_number)) modified = re.sub(pattern, get_replacement, source, flags=re.MULTILINE) From 80eced1caf723f4993c372436791da039a08f900 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 18:15:09 +0100 Subject: [PATCH 23/33] Remove commented-out copied code. [skip ci] --- bin/update_version_number_in_files.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 2820f82d92..94c15caba4 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -144,30 +144,6 @@ def get_replacement(match): return modified - # """Apply all fixes in this module""" - # modified = copy.deepcopy(source) - # - # for replacement in replacements: - # if replacement["new"] is None: - # print( - # "'%s' has been removed, please delete from your code" - # % replacement["old"] - # ) - # continue - # - # modified = fix_include_version_header( - # replacement["old"], replacement["headers"], modified - # ) - # if replacement["macro"] and replacement["always_defined"]: - # modified = fix_always_defined_macros( - # replacement["old"], replacement["new"], modified - # ) - # elif replacement["always_defined"]: - # modified = fix_ifdefs(replacement["old"], modified) - # modified = fix_replacement(replacement["old"], replacement["new"], modified) - # - # return modified - def yes_or_no(question): """Convert user input from yes/no variations to True/False""" From e9686f29262d22e99692416ae53bc54e8f47a9c9 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 19:08:43 +0100 Subject: [PATCH 24/33] Add command line argument for the new version number. [skip ci] --- bin/update_version_number_in_files.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 94c15caba4..1cb8e948f3 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -190,10 +190,11 @@ def create_patch(filename, original, modified): parser.add_argument( "--patch-only", "-p", action="store_true", help="Print the patches and exit" ) + parser.add_argument("new_version", help="Specify the new version number") args = parser.parse_args() if args.force and args.patch_only: raise ValueError("Incompatible options: --force and --patch") - - bump_version_numbers(new_version_number=VersionNumber(63, 15, 12)) + major, minor, patch = map(int, args.new_version.split(".")) + bump_version_numbers(new_version_number=VersionNumber(major, minor, patch)) From 6de38261b42417158eb08f84baedb55a5dcffde6 Mon Sep 17 00:00:00 2001 From: tomc271 Date: Mon, 21 Aug 2023 19:09:21 +0100 Subject: [PATCH 25/33] Corrected regex. [skip ci] --- bin/update_version_number_in_files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 1cb8e948f3..ef0eaf3eb4 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -99,7 +99,7 @@ def bump_version_numbers(new_version_number): ) update_version_number_in_file( "tools/pylib/_boutpp_build/backend.py", - r"_bout_next_version = \"v(\d+\.\d+\.\d+)\"", + r"_bout_next_version = \"(\d+\.\d+\.\d+)\"", bout_next_version_number, ) From a932ba08621c9c4d9c848d953b6ec7c4d9207fff Mon Sep 17 00:00:00 2001 From: tomc271 Date: Tue, 22 Aug 2023 13:18:40 +0100 Subject: [PATCH 26/33] Add command line description to update_version_number_in_file script. [skip ci] --- bin/update_version_number_in_files.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index ef0eaf3eb4..c2299f781b 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -176,7 +176,18 @@ def create_patch(filename, original, modified): formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent( """\ - TODO: Description here... + Update the software version number to the specified version, + to be given in the form major.minor.patch, + e.g. 5.10.3 + + Where the 3rd ('patch') part of the version is omitted, + only the 'major' and 'minor' parts will be used, + e.g. 5.10.3 -> 5.10 + + For the 'bout-next' version number, + the 'minor' version number of the provided version will be incremented by 1, + e.g. 5.10.3 -> 5.11.3 + """ ), ) From d52d622721939b3d62cc86db1427b2067633dd8c Mon Sep 17 00:00:00 2001 From: tomc271 Date: Tue, 22 Aug 2023 13:36:55 +0100 Subject: [PATCH 27/33] Make ShortVersionNumber a dataclass. [skip ci] --- bin/update_version_number_in_files.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index c2299f781b..e414df14b0 100644 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -114,10 +114,10 @@ def __str__(self): return "%d.%d.%d" % (self.major_version, self.minor_version, self.patch_version) +@dataclass class ShortVersionNumber: - def __init__(self, major_version, minor_version): - self.major_version = major_version - self.minor_version = minor_version + major_version: int + minor_version: int def __str__(self): return "%d.%d" % (self.major_version, self.minor_version) From 19fe7ed5624be60f30c82c9bd4a3b16955e11530 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 20 Sep 2023 14:18:19 +0100 Subject: [PATCH 28/33] Maint: Make update version/citation scripts executable [skip ci] --- bin/update_citations.py | 1 + bin/update_version_number_in_files.py | 1 + 2 files changed, 2 insertions(+) mode change 100644 => 100755 bin/update_citations.py mode change 100644 => 100755 bin/update_version_number_in_files.py diff --git a/bin/update_citations.py b/bin/update_citations.py old mode 100644 new mode 100755 index a672eac343..56ae990bf9 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 import subprocess from pathlib import Path import os diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py old mode 100644 new mode 100755 index e414df14b0..7d184af0bf --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 from dataclasses import dataclass from pathlib import Path import argparse From 5a8375ca6d1801481532b5a3e10658f0a5f56043 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 20 Sep 2023 14:19:20 +0100 Subject: [PATCH 29/33] Maint: Fix typo in next version regex --- bin/update_version_number_in_files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 7d184af0bf..de187c413a 100755 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -100,7 +100,7 @@ def bump_version_numbers(new_version_number): ) update_version_number_in_file( "tools/pylib/_boutpp_build/backend.py", - r"_bout_next_version = \"(\d+\.\d+\.\d+)\"", + r"_bout_next_version = \"v(\d+\.\d+\.\d+)\"", bout_next_version_number, ) From a7f057e832faf52034ee1aef52b1792495c9bd3c Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 20 Sep 2023 14:38:39 +0100 Subject: [PATCH 30/33] Maint: Update list of known authors in citation script --- bin/update_citations.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index 56ae990bf9..d85bcffb15 100755 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -217,8 +217,10 @@ class KnownAuthor(NamedTuple): "Michael Loiten Magnussen": KnownAuthor("Løiten", "Michael"), "Maxim Umansky - x26041": KnownAuthor("Umansky", "Maxim"), "nick-walkden": KnownAuthor("Walkden", "Nicholas"), - "ZedThree": KnownAuthor("Hill", "Peter") - # "tomc271": KnownAuthor("Chapman", "Tom") + "ZedThree": KnownAuthor("Hill", "Peter"), + "tomc271": KnownAuthor("Chapman", "Tom"), + "j-b-o": KnownAuthor("Omotani", "John"), + "BS": KnownAuthor("Brendan", "Shanahan"), } if __name__ == "__main__": From bc1b436c3c08ba3f0b7671d4f24205ddba7b3d58 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 20 Sep 2023 14:39:19 +0100 Subject: [PATCH 31/33] Maint: Don't print anything if no unrecognised authors --- bin/update_citations.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bin/update_citations.py b/bin/update_citations.py index d85bcffb15..607227775d 100755 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -195,6 +195,9 @@ def update_citations(): if not author_found_in_existing_authors(a[0], existing_authors) ] + if not unrecognised_authors: + return + print("The following authors were not recognised. Add to citations?") for author in unrecognised_authors: print(author) From a3fa61cb8da880e56603f2b44c421ac09b869ea3 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 20 Sep 2023 14:41:20 +0100 Subject: [PATCH 32/33] Add Tom Chapman to authors list [skip ci] --- CITATION.cff | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CITATION.cff b/CITATION.cff index cdd0f7bf2f..dd2650a0eb 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -185,6 +185,9 @@ authors: - family-names: James given-names: Toby + - family-names: Tom + given-names: Chapman + version: 5.0.0 date-released: 2023-02-08 repository-code: https://github.com/boutproject/BOUT-dev From 33632adb2f3406b02ab569d19ee84078e457e16a Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 20 Sep 2023 15:25:33 +0100 Subject: [PATCH 33/33] Fix typo in author list [skip ci] Co-authored-by: David Bold --- bin/update_citations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/update_citations.py b/bin/update_citations.py index 607227775d..ed358f7592 100755 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -209,7 +209,7 @@ class KnownAuthor(NamedTuple): KNOWN_AUTHORS = { - "bendudson": KnownAuthor("Dodson", "Benjamin"), + "bendudson": KnownAuthor("Dudson", "Benjamin"), "brey": KnownAuthor("Breyiannis", "George"), "David Schwörer": KnownAuthor("Bold", "David"), "dschwoerer": KnownAuthor("Bold", "David"),