fix_headers.py
1 #!/usr/bin/env python 2 3 ############################ Copyrights and license ############################ 4 # # 5 # Copyright 2013 Vincent Jacques <vincent@vincent-jacques.net> # 6 # Copyright 2014 Vincent Jacques <vincent@vincent-jacques.net> # 7 # Copyright 2016 Peter Buckley <dx-pbuckley@users.noreply.github.com> # 8 # Copyright 2018 sfdye <tsfdye@gmail.com> # 9 # Copyright 2019 Steve Kowalik <steven@wedontsleep.org> # 10 # Copyright 2019 Wan Liuyang <tsfdye@gmail.com> # 11 # Copyright 2020 Steve Kowalik <steven@wedontsleep.org> # 12 # Copyright 2020 Wan Liuyang <tsfdye@gmail.com> # 13 # # 14 # This file is part of PyGithub. # 15 # http://pygithub.readthedocs.io/ # 16 # # 17 # PyGithub is free software: you can redistribute it and/or modify it under # 18 # the terms of the GNU Lesser General Public License as published by the Free # 19 # Software Foundation, either version 3 of the License, or (at your option) # 20 # any later version. # 21 # # 22 # PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY # 23 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # 24 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # 25 # details. # 26 # # 27 # You should have received a copy of the GNU Lesser General Public License # 28 # along with PyGithub. If not, see <http://www.gnu.org/licenses/>. # 29 # # 30 ################################################################################ 31 32 import os 33 import subprocess 34 35 eightySharps = "#" * 80 36 37 38 def generateLicenseSection(filename): 39 yield "############################ Copyrights and license ############################" 40 yield "# #" 41 for year, name in sorted(listContributors(filename)): 42 line = "# Copyright " + year + " " + name 43 line += (79 - len(line)) * " " + "#" 44 yield line 45 yield "# #" 46 yield "# This file is part of PyGithub. #" 47 yield "# http://pygithub.readthedocs.io/ #" 48 yield "# #" 49 yield "# PyGithub is free software: you can redistribute it and/or modify it under #" 50 yield "# the terms of the GNU Lesser General Public License as published by the Free #" 51 yield "# Software Foundation, either version 3 of the License, or (at your option) #" 52 yield "# any later version. #" 53 yield "# #" 54 yield "# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #" 55 yield "# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #" 56 yield "# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #" 57 yield "# details. #" 58 yield "# #" 59 yield "# You should have received a copy of the GNU Lesser General Public License #" 60 yield "# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #" 61 yield "# #" 62 yield "################################################################################" 63 64 65 def listContributors(filename): 66 contributors = set() 67 result = subprocess.check_output( 68 ["git", "log", "--format=format:%ad %an <%ae>", "--date=short", "--", filename], 69 text=True, 70 ) 71 for line in result.split("\n"): 72 year = line[0:4] 73 name = line[11:] 74 contributors.add((year, name)) 75 return contributors 76 77 78 def extractBodyLines(lines): 79 bodyLines = [] 80 81 seenEndOfHeader = False 82 83 for line in lines: 84 if len(line) > 0 and line[0] != "#": 85 seenEndOfHeader = True 86 if seenEndOfHeader: 87 bodyLines.append(line) 88 # else: 89 # print "HEAD:", line 90 if line == eightySharps: 91 seenEndOfHeader = True 92 93 # print "BODY:", "\nBODY: ".join(bodyLines) 94 95 return bodyLines 96 97 98 class PythonHeader: 99 def fix(self, filename, lines): 100 isExecutable = lines[0].startswith("#!") 101 newLines = [] 102 103 if isExecutable: 104 newLines.append("#!/usr/bin/env python") 105 newLines.append("# -*- coding: utf-8 -*-") 106 newLines.append("") 107 108 for line in generateLicenseSection(filename): 109 newLines.append(line) 110 111 bodyLines = extractBodyLines(lines) 112 113 if len(bodyLines) > 0 and bodyLines[0] != "": 114 newLines.append("") 115 if "import " not in bodyLines[0] and bodyLines[0] != '"""' and not bodyLines[0].startswith("##########"): 116 newLines.append("") 117 newLines += bodyLines 118 119 return newLines 120 121 122 class StandardHeader: 123 def fix(self, filename, lines): 124 newLines = [] 125 126 for line in generateLicenseSection(filename): 127 newLines.append(line) 128 129 bodyLines = extractBodyLines(lines) 130 131 if len(bodyLines) > 0 and bodyLines[0] != "": 132 newLines.append("") 133 newLines += bodyLines 134 135 return newLines 136 137 138 def findHeadersAndFiles(): 139 for root, dirs, files in os.walk(".", topdown=True): 140 if ".git" in dirs: 141 dirs.remove(".git") 142 if "developer.github.com" in dirs: 143 dirs.remove("developer.github.com") 144 if "build" in dirs: 145 dirs.remove("build") 146 if ".tox" in dirs: 147 dirs.remove(".tox") 148 if ".venv" in dirs: 149 dirs.remove(".venv") 150 if "PyGithub.egg-info" in dirs: 151 dirs.remove("PyGithub.egg-info") 152 153 for filename in files: 154 fullname = os.path.join(root, filename) 155 if filename == "GithubCredentials.py": 156 pass 157 elif filename.endswith(".py"): 158 yield (PythonHeader(), fullname) 159 elif filename in ["COPYING", "COPYING.LESSER"]: 160 pass 161 elif filename.endswith(".rst") or filename.endswith(".md"): 162 pass 163 elif filename == ".gitignore": 164 yield (StandardHeader(), fullname) 165 elif "ReplayData" in fullname: 166 pass 167 elif fullname.endswith(".pyc"): 168 pass 169 else: 170 print("Don't know what to do with", filename) 171 172 173 def main(): 174 for header, filename in findHeadersAndFiles(): 175 print("Analyzing", filename) 176 with open(filename) as f: 177 lines = list(line.rstrip() for line in f) 178 newLines = header.fix(filename, lines) 179 if newLines != lines: 180 print(" => actually modifying", filename) 181 with open(filename, "w") as f: 182 for line in newLines: 183 f.write(line + "\n") 184 185 186 if __name__ == "__main__": 187 main()