From c50f38ed0e6819bd9e8a7020dbf239fb8327ab28 Mon Sep 17 00:00:00 2001 From: Olli Meier Date: Wed, 9 Feb 2022 17:06:02 +0100 Subject: [PATCH 1/9] extend name table with getFamilyName, getSubFamilyName and getNiceFullName + unittests --- .gitignore | 1 + Lib/fontTools/ttLib/tables/_n_a_m_e.py | 31 ++++++++ Tests/Snippets/rename-fonts_test.py | 50 +++++++++++++ Tests/ttLib/tables/_n_a_m_e_test.py | 97 ++++++++++++++++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 Tests/Snippets/rename-fonts_test.py diff --git a/.gitignore b/.gitignore index eba633edf..9c564fd54 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ dist/ *.egg-info/ *.egg MANIFEST +.idea # Installer logs pip-log.txt diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py index b51e1b725..b9a87e579 100644 --- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py +++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py @@ -120,6 +120,37 @@ class table__n_a_m_e(DefaultTable.DefaultTable): else: return None + def getFamilyName(self): + for nameID in (21, 16, 1): + name = self.getDebugName(nameID) + if name is not None: + return name + return None + + def getSubFamilyName(self): + for nameID in (22, 17, 2): + name = self.getDebugName(nameID) + if name is not None: + return name + return None + + def getNiceFullName(self): + for nameIDs in ((21, 22), (16, 17), (1, 2), (4, ), (6, )): + if len(nameIDs) == 2: + name_fam = self.getDebugName(nameIDs[0]) + name_subfam = self.getDebugName(nameIDs[1]) + if None in [name_fam, name_subfam]: + continue # if any if None, skip + name = f"{name_fam} {name_subfam}" + if name_subfam.lower() == 'regular': + name = f"{name_fam}" + return name + else: + name = self.getDebugName(nameIDs[0]) + if name is not None: + return name + return None + def setName(self, string, nameID, platformID, platEncID, langID): """ Set the 'string' for the name record identified by 'nameID', 'platformID', 'platEncID' and 'langID'. If a record with that nameID doesn't exist, create it diff --git a/Tests/Snippets/rename-fonts_test.py b/Tests/Snippets/rename-fonts_test.py new file mode 100644 index 000000000..86438b5df --- /dev/null +++ b/Tests/Snippets/rename-fonts_test.py @@ -0,0 +1,50 @@ +# coding=utf-8 + +""" +This test is for checking function within rename-fonts + +""" + +import unittest +from fontTools.misc.testTools import FakeFont +from fontTools.ttLib.tables._n_a_m_e import (table__n_a_m_e, NameRecord, nameRecordFormat, nameRecordSize, makeName, log) +from fonttools.Snippets.rename-fonts import get_current_family_name + + +class RenameFontsTest(unittest.TestCase): + @classmethod + def setUp(self): + self.font = FakeFont(['.nodef', 'A', 'B', 'C']) + name = table__n_a_m_e() + name.names = [ + makeName("Copyright", 0, 1, 0, 0), + makeName("Family Name ID 1", 1, 1, 0, 0), + makeName("SubFamily Name ID 2", 2, 1, 0, 0), + makeName("Unique Name ID 3", 3, 1, 0, 0), + makeName("Full Name ID 4", 4, 1, 0, 0), + makeName("PS Name ID 6", 6, 1, 0, 0), + makeName("Version Name ID 5", 5, 1, 0, 0), + makeName("Trademark Name ID 7", 7, 1, 0, 0), + ] + + self.font['name'] = name + + def test_get_current_family_name(self): + + result_value = get_current_family_name(self.font['name']) + self.assertEqual("Family Name ID 1", result_value) + + expected_value = "Family Name ID 16" + self.font['name'].setName(expected_value, 16, 1, 0, 0) + result_value = get_current_family_name(self.font['name']) + self.assertEqual(expected_value, result_value) + + expected_value = "Family Name ID 21" + self.font['name'].setName(expected_value, 21, 1, 0, 0) + result_value = get_current_family_name(self.font['name']) + self.assertEqual(expected_value, result_value) + + +if __name__ == "__main__": + import sys + sys.exit(unittest.main()) diff --git a/Tests/ttLib/tables/_n_a_m_e_test.py b/Tests/ttLib/tables/_n_a_m_e_test.py index 38fbfef78..93c728eb7 100644 --- a/Tests/ttLib/tables/_n_a_m_e_test.py +++ b/Tests/ttLib/tables/_n_a_m_e_test.py @@ -450,6 +450,103 @@ class NameRecordTest(unittest.TestCase): self.assertEqual(name.getEncoding(None), None) self.assertEqual(name.getEncoding(default=None), None) + def test_get_family_name(self): + name = table__n_a_m_e() + name.names = [ + makeName("Copyright", 0, 1, 0, 0), + makeName("Family Name ID 1", 1, 1, 0, 0), + makeName("SubFamily Name ID 2", 2, 1, 0, 0), + makeName("Unique Name ID 3", 3, 1, 0, 0), + makeName("Full Name ID 4", 4, 1, 0, 0), + makeName("PS Name ID 6", 6, 1, 0, 0), + makeName("Version Name ID 5", 5, 1, 0, 0), + makeName("Trademark Name ID 7", 7, 1, 0, 0), + ] + + result_value = name.getFamilyName() + self.assertEqual("Family Name ID 1", result_value) + + expected_value = "Family Name ID 16" + name.setName(expected_value, 16, 1, 0, 0) + result_value = name.getFamilyName() + self.assertEqual(expected_value, result_value) + + expected_value = "Family Name ID 21" + name.setName(expected_value, 21, 1, 0, 0) + result_value = name.getFamilyName() + self.assertEqual(expected_value, result_value) + + def test_get_subfamily_name(self): + name = table__n_a_m_e() + name.names = [ + makeName("Copyright", 0, 1, 0, 0), + makeName("Family Name ID 1", 1, 1, 0, 0), + makeName("SubFamily Name ID 2", 2, 1, 0, 0), + makeName("Unique Name ID 3", 3, 1, 0, 0), + makeName("Full Name ID 4", 4, 1, 0, 0), + makeName("PS Name ID 6", 6, 1, 0, 0), + makeName("Version Name ID 5", 5, 1, 0, 0), + makeName("Trademark Name ID 7", 7, 1, 0, 0), + ] + + result_value = name.getSubFamilyName() + self.assertEqual("Family Name ID 2", result_value) + + expected_value = "Family Name ID 17" + name.setName(expected_value, 16, 1, 0, 0) + result_value = name.getSubFamilyName() + self.assertEqual(expected_value, result_value) + + expected_value = "Family Name ID 22" + name.setName(expected_value, 21, 1, 0, 0) + result_value = name.getSubFamilyName() + self.assertEqual(expected_value, result_value) + + def test_get_nice_full_name(self): + name = table__n_a_m_e() + name.names = [ + makeName("NID 1", 1, 1, 0, 0), + makeName("NID 2", 2, 1, 0, 0), + makeName("NID 4", 4, 1, 0, 0), + makeName("NID 6", 6, 1, 0, 0), + ] + + result_value = name.getNiceFullName() + self.assertEqual("NID 1 NID 2", result_value) + + expected_value = "NID 1 NID 2" + # expection is still NID 1 NID 2, + # because name ID 17 is missing + name.setName("NID 16", 16, 1, 0, 0) + result_value = name.getNiceFullName() + self.assertEqual(expected_value, result_value) + + name.setName('NID 17', 17, 1, 0, 0) + result_value = name.getNiceFullName() + self.assertEqual("NID 16 NID 17", result_value) + + expected_value = "NID 16 NID 17" + # expection is still NID 16 NID 17, + # because name ID 21 is missing + name.setName('NID 21', 21, 1, 0, 0) + result_value = name.getNiceFullName() + self.assertEqual(expected_value, result_value) + + name.setName('NID 22', 22, 1, 0, 0) + result_value = name.getNiceFullName() + self.assertEqual("NID 21 NID 22", result_value) + + for NID in [2, 16, 17, 21, 22]: + name.removeNames(NID) + + result_value = name.getNiceFullName() + self.assertEqual("NID 4", result_value) + + name.setName('Regular', 2, 1, 0, 0) + result_value = name.getNiceFullName() + self.assertEqual("NID 1", result_value) + + if __name__ == "__main__": import sys sys.exit(unittest.main()) From 17f4b767149915ca1addaf9b7d2ace10d1e5c478 Mon Sep 17 00:00:00 2001 From: Olli Meier Date: Wed, 9 Feb 2022 17:07:32 +0100 Subject: [PATCH 2/9] remove wrong test file. --- Tests/Snippets/rename-fonts_test.py | 50 ----------------------------- 1 file changed, 50 deletions(-) delete mode 100644 Tests/Snippets/rename-fonts_test.py diff --git a/Tests/Snippets/rename-fonts_test.py b/Tests/Snippets/rename-fonts_test.py deleted file mode 100644 index 86438b5df..000000000 --- a/Tests/Snippets/rename-fonts_test.py +++ /dev/null @@ -1,50 +0,0 @@ -# coding=utf-8 - -""" -This test is for checking function within rename-fonts - -""" - -import unittest -from fontTools.misc.testTools import FakeFont -from fontTools.ttLib.tables._n_a_m_e import (table__n_a_m_e, NameRecord, nameRecordFormat, nameRecordSize, makeName, log) -from fonttools.Snippets.rename-fonts import get_current_family_name - - -class RenameFontsTest(unittest.TestCase): - @classmethod - def setUp(self): - self.font = FakeFont(['.nodef', 'A', 'B', 'C']) - name = table__n_a_m_e() - name.names = [ - makeName("Copyright", 0, 1, 0, 0), - makeName("Family Name ID 1", 1, 1, 0, 0), - makeName("SubFamily Name ID 2", 2, 1, 0, 0), - makeName("Unique Name ID 3", 3, 1, 0, 0), - makeName("Full Name ID 4", 4, 1, 0, 0), - makeName("PS Name ID 6", 6, 1, 0, 0), - makeName("Version Name ID 5", 5, 1, 0, 0), - makeName("Trademark Name ID 7", 7, 1, 0, 0), - ] - - self.font['name'] = name - - def test_get_current_family_name(self): - - result_value = get_current_family_name(self.font['name']) - self.assertEqual("Family Name ID 1", result_value) - - expected_value = "Family Name ID 16" - self.font['name'].setName(expected_value, 16, 1, 0, 0) - result_value = get_current_family_name(self.font['name']) - self.assertEqual(expected_value, result_value) - - expected_value = "Family Name ID 21" - self.font['name'].setName(expected_value, 21, 1, 0, 0) - result_value = get_current_family_name(self.font['name']) - self.assertEqual(expected_value, result_value) - - -if __name__ == "__main__": - import sys - sys.exit(unittest.main()) From 1a16b24cd53061256c7e50c5a3a55b3d611c60f2 Mon Sep 17 00:00:00 2001 From: Olli Meier Date: Wed, 9 Feb 2022 17:17:11 +0100 Subject: [PATCH 3/9] Fix unittests --- Tests/ttLib/tables/_n_a_m_e_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/ttLib/tables/_n_a_m_e_test.py b/Tests/ttLib/tables/_n_a_m_e_test.py index 93c728eb7..4495738dc 100644 --- a/Tests/ttLib/tables/_n_a_m_e_test.py +++ b/Tests/ttLib/tables/_n_a_m_e_test.py @@ -490,15 +490,15 @@ class NameRecordTest(unittest.TestCase): ] result_value = name.getSubFamilyName() - self.assertEqual("Family Name ID 2", result_value) + self.assertEqual("SubFamily Name ID 2", result_value) expected_value = "Family Name ID 17" - name.setName(expected_value, 16, 1, 0, 0) + name.setName(expected_value, 17, 1, 0, 0) result_value = name.getSubFamilyName() self.assertEqual(expected_value, result_value) expected_value = "Family Name ID 22" - name.setName(expected_value, 21, 1, 0, 0) + name.setName(expected_value, 22, 1, 0, 0) result_value = name.getSubFamilyName() self.assertEqual(expected_value, result_value) From 79360a30bf017740f6bff1a8cc64a81275822da9 Mon Sep 17 00:00:00 2001 From: Olli Meier Date: Wed, 9 Feb 2022 17:34:40 +0100 Subject: [PATCH 4/9] Refactor based on feedback. --- Lib/fontTools/ttLib/tables/_n_a_m_e.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py index b9a87e579..68e03b56a 100644 --- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py +++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py @@ -120,19 +120,22 @@ class table__n_a_m_e(DefaultTable.DefaultTable): else: return None - def getFamilyName(self): - for nameID in (21, 16, 1): - name = self.getDebugName(nameID) - if name is not None: - return name + def getBestDebugName(self, nameID): + for group in ((21, 16, 1), (22, 17, 2), (25, 4, 6)): + if nameID not in group: + continue + + for fallback_id in group: + name = self.getDebugName(fallback_id) + if name: + return name return None + def getFamilyName(self): + return self.getBestDebugName(21) + def getSubFamilyName(self): - for nameID in (22, 17, 2): - name = self.getDebugName(nameID) - if name is not None: - return name - return None + return self.getBestDebugName(22) def getNiceFullName(self): for nameIDs in ((21, 22), (16, 17), (1, 2), (4, ), (6, )): From 6d05b938beddbabe801777db85f5cf142a28d279 Mon Sep 17 00:00:00 2001 From: Olli Meier Date: Wed, 9 Feb 2022 17:40:28 +0100 Subject: [PATCH 5/9] fix typo --- Lib/fontTools/ttLib/tables/_n_a_m_e.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py index 68e03b56a..0f3f5f0fa 100644 --- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py +++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py @@ -143,7 +143,7 @@ class table__n_a_m_e(DefaultTable.DefaultTable): name_fam = self.getDebugName(nameIDs[0]) name_subfam = self.getDebugName(nameIDs[1]) if None in [name_fam, name_subfam]: - continue # if any if None, skip + continue # if any is None, skip name = f"{name_fam} {name_subfam}" if name_subfam.lower() == 'regular': name = f"{name_fam}" From 1649973800cbf0c8d96b785ccac381672a0996cc Mon Sep 17 00:00:00 2001 From: Olli Meier Date: Wed, 9 Feb 2022 17:44:28 +0100 Subject: [PATCH 6/9] Make use of 'Best' in names --- Lib/fontTools/ttLib/tables/_n_a_m_e.py | 6 +++--- Tests/ttLib/tables/_n_a_m_e_test.py | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py index 0f3f5f0fa..9574898ef 100644 --- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py +++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py @@ -131,13 +131,13 @@ class table__n_a_m_e(DefaultTable.DefaultTable): return name return None - def getFamilyName(self): + def getBestFamilyName(self): return self.getBestDebugName(21) - def getSubFamilyName(self): + def getBestSubFamilyName(self): return self.getBestDebugName(22) - def getNiceFullName(self): + def getBestFullName(self): for nameIDs in ((21, 22), (16, 17), (1, 2), (4, ), (6, )): if len(nameIDs) == 2: name_fam = self.getDebugName(nameIDs[0]) diff --git a/Tests/ttLib/tables/_n_a_m_e_test.py b/Tests/ttLib/tables/_n_a_m_e_test.py index 4495738dc..a9a641dd8 100644 --- a/Tests/ttLib/tables/_n_a_m_e_test.py +++ b/Tests/ttLib/tables/_n_a_m_e_test.py @@ -463,17 +463,17 @@ class NameRecordTest(unittest.TestCase): makeName("Trademark Name ID 7", 7, 1, 0, 0), ] - result_value = name.getFamilyName() + result_value = name.getBestFamilyName() self.assertEqual("Family Name ID 1", result_value) expected_value = "Family Name ID 16" name.setName(expected_value, 16, 1, 0, 0) - result_value = name.getFamilyName() + result_value = name.getBestFamilyName() self.assertEqual(expected_value, result_value) expected_value = "Family Name ID 21" name.setName(expected_value, 21, 1, 0, 0) - result_value = name.getFamilyName() + result_value = name.getBestFamilyName() self.assertEqual(expected_value, result_value) def test_get_subfamily_name(self): @@ -489,17 +489,17 @@ class NameRecordTest(unittest.TestCase): makeName("Trademark Name ID 7", 7, 1, 0, 0), ] - result_value = name.getSubFamilyName() + result_value = name.getBestSubFamilyName() self.assertEqual("SubFamily Name ID 2", result_value) expected_value = "Family Name ID 17" name.setName(expected_value, 17, 1, 0, 0) - result_value = name.getSubFamilyName() + result_value = name.getBestSubFamilyName() self.assertEqual(expected_value, result_value) expected_value = "Family Name ID 22" name.setName(expected_value, 22, 1, 0, 0) - result_value = name.getSubFamilyName() + result_value = name.getBestSubFamilyName() self.assertEqual(expected_value, result_value) def test_get_nice_full_name(self): @@ -511,39 +511,39 @@ class NameRecordTest(unittest.TestCase): makeName("NID 6", 6, 1, 0, 0), ] - result_value = name.getNiceFullName() + result_value = name.getBestFullName() self.assertEqual("NID 1 NID 2", result_value) expected_value = "NID 1 NID 2" # expection is still NID 1 NID 2, # because name ID 17 is missing name.setName("NID 16", 16, 1, 0, 0) - result_value = name.getNiceFullName() + result_value = name.getBestFullName() self.assertEqual(expected_value, result_value) name.setName('NID 17', 17, 1, 0, 0) - result_value = name.getNiceFullName() + result_value = name.getBestFullName() self.assertEqual("NID 16 NID 17", result_value) expected_value = "NID 16 NID 17" # expection is still NID 16 NID 17, # because name ID 21 is missing name.setName('NID 21', 21, 1, 0, 0) - result_value = name.getNiceFullName() + result_value = name.getBestFullName() self.assertEqual(expected_value, result_value) name.setName('NID 22', 22, 1, 0, 0) - result_value = name.getNiceFullName() + result_value = name.getBestFullName() self.assertEqual("NID 21 NID 22", result_value) for NID in [2, 16, 17, 21, 22]: name.removeNames(NID) - result_value = name.getNiceFullName() + result_value = name.getBestFullName() self.assertEqual("NID 4", result_value) name.setName('Regular', 2, 1, 0, 0) - result_value = name.getNiceFullName() + result_value = name.getBestFullName() self.assertEqual("NID 1", result_value) From 8dddbf4a61004dabec9b5e81abedaa2aac466b8d Mon Sep 17 00:00:00 2001 From: Olli Meier Date: Wed, 9 Feb 2022 17:48:51 +0100 Subject: [PATCH 7/9] refactor based on feedback. --- Lib/fontTools/ttLib/tables/_n_a_m_e.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py index 9574898ef..d552ffdd9 100644 --- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py +++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py @@ -120,22 +120,18 @@ class table__n_a_m_e(DefaultTable.DefaultTable): else: return None - def getBestDebugName(self, nameID): - for group in ((21, 16, 1), (22, 17, 2), (25, 4, 6)): - if nameID not in group: - continue - - for fallback_id in group: - name = self.getDebugName(fallback_id) - if name: - return name + def getBestDebugName(self, nameIDs): + for nameID in nameIDs: + name = self.getDebugName(nameID) + if name is not None: + return name return None def getBestFamilyName(self): - return self.getBestDebugName(21) + return self.getBestDebugName((21, 16, 1)) def getBestSubFamilyName(self): - return self.getBestDebugName(22) + return self.getBestDebugName((22, 17, 2)) def getBestFullName(self): for nameIDs in ((21, 22), (16, 17), (1, 2), (4, ), (6, )): From 5986107b43d12ad6ac484b45955baecfcd6f61c0 Mon Sep 17 00:00:00 2001 From: Olli Meier Date: Wed, 9 Feb 2022 17:57:55 +0100 Subject: [PATCH 8/9] based on feedback: Change name and add comments. --- Lib/fontTools/ttLib/tables/_n_a_m_e.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py index d552ffdd9..9558addbb 100644 --- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py +++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py @@ -120,7 +120,7 @@ class table__n_a_m_e(DefaultTable.DefaultTable): else: return None - def getBestDebugName(self, nameIDs): + def getFirstDebugName(self, nameIDs): for nameID in nameIDs: name = self.getDebugName(nameID) if name is not None: @@ -128,18 +128,26 @@ class table__n_a_m_e(DefaultTable.DefaultTable): return None def getBestFamilyName(self): - return self.getBestDebugName((21, 16, 1)) + # 21 = WWS Family Name + # 16 = Typographic Family Name + # 1 = Family Name + return self.getFirstDebugName((21, 16, 1)) def getBestSubFamilyName(self): - return self.getBestDebugName((22, 17, 2)) + # 22 = WWS SubFamily Name + # 17 = Typographic SubFamily Name + # 2 = SubFamily Name + return self.getFirstDebugName((22, 17, 2)) def getBestFullName(self): + # 4 = Full Name + # 6 = PostScript Name for nameIDs in ((21, 22), (16, 17), (1, 2), (4, ), (6, )): if len(nameIDs) == 2: name_fam = self.getDebugName(nameIDs[0]) name_subfam = self.getDebugName(nameIDs[1]) if None in [name_fam, name_subfam]: - continue # if any is None, skip + continue # if any is None, skip name = f"{name_fam} {name_subfam}" if name_subfam.lower() == 'regular': name = f"{name_fam}" From 8e0aad595d831fff4b347c9e0f629b537fe32f5b Mon Sep 17 00:00:00 2001 From: Olli Meier Date: Wed, 9 Feb 2022 18:04:12 +0100 Subject: [PATCH 9/9] Added my name to the list of contributers. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 6a35807cf..fdff24334 100644 --- a/README.rst +++ b/README.rst @@ -248,7 +248,7 @@ Simon Daniels, Peter Dekkers, Behdad Esfahbod, Behnam Esfahbod, Hannes Famira, Sam Fishman, Matt Fontaine, Takaaki Fuji, Yannis Haralambous, Greg Hitchcock, Jeremie Hornus, Khaled Hosny, John Hudson, Denis Moyogo Jacquerye, Jack Jansen, Tom Kacvinsky, Jens Kutilek, Antoine Leca, Werner Lemberg, Tal -Leming, Peter Lofting, Cosimo Lupo, Masaya Nakamura, Dave Opstad, +Leming, Peter Lofting, Cosimo Lupo, Olli Meier, Masaya Nakamura, Dave Opstad, Laurence Penney, Roozbeh Pournader, Garret Rieger, Read Roberts, Guido van Rossum, Just van Rossum, Andreas Seidel, Georg Seifert, Chris Simpkins, Miguel Sousa, Adam Twardoch, Adrien Tétar, Vitaly Volkov,