@@ -6,10 +6,13 @@ derives the authorship and copyright years information from the git history
66of the project; hence, this script must be run within a git repository.
77"""
88
9+ from __future__ import annotations
10+
911import os
1012import sys
1113assert sys .version_info >= (3 , 6 ), 'Script requires Python 3.6+'
1214import tempfile
15+ import typing as t
1316from argparse import ArgumentParser , Namespace
1417from configparser import ConfigParser
1518from operator import attrgetter
@@ -18,14 +21,13 @@ from datetime import datetime
1821from subprocess import Popen , PIPE , DEVNULL
1922from pathlib import Path
2023from fnmatch import fnmatch
21- from typing import NamedTuple , Iterator , List , Tuple , Set , Union , Optional
2224
2325
2426SPDX_PREFIX = 'SPDX-License-Identifier:'
2527COPYRIGHT_PREFIX = 'Copyright (c)'
2628
2729
28- def main (args : List [str ] = None ):
30+ def main (args : t . List [str ] = None ):
2931 if args is None :
3032 args = sys .argv [1 :]
3133 config = get_config (args )
@@ -41,7 +43,7 @@ def main(args: List[str] = None):
4143 target .write (chunk )
4244
4345
44- def get_config (args : List [str ]) -> Namespace :
46+ def get_config (args : t . List [str ]) -> Namespace :
4547 config = ConfigParser (
4648 defaults = {
4749 'include' : '**/*' ,
@@ -52,10 +54,10 @@ def get_config(args: List[str]) -> Namespace:
5254 'spdx_prefix' : SPDX_PREFIX ,
5355 'copy_prefix' : COPYRIGHT_PREFIX ,
5456 },
55- delimiters = ('=' ,), default_section = 'settings' ,
57+ delimiters = ('=' ,), default_section = 'copyrights: settings' ,
5658 empty_lines_in_values = False , interpolation = None ,
5759 converters = {'list' : lambda s : s .strip ().splitlines () })
58- config .read ('copyrights .cfg' )
60+ config .read ('setup .cfg' )
5961 sect = config [config .default_section ]
6062
6163 parser = ArgumentParser (description = __doc__ )
@@ -113,10 +115,10 @@ def get_config(args: List[str]) -> Namespace:
113115 return ns
114116
115117
116- class Copyright (NamedTuple ):
118+ class Copyright (t . NamedTuple ):
117119 author : str
118120 email : str
119- years : Set [int ]
121+ years : t . Set [int ]
120122
121123 def __str__ (self ):
122124 if len (self .years ) > 1 :
@@ -126,8 +128,8 @@ class Copyright(NamedTuple):
126128 return f'{ years } { self .author } <{ self .email } >'
127129
128130
129- def get_copyrights (include : Set [str ], exclude : Set [str ])\
130- -> Iterator [Tuple [Path , List [Copyright ]]]:
131+ def get_copyrights (include : t . Set [str ], exclude : t . Set [str ])\
132+ -> t . Iterator [t . Tuple [Path , t . Container [Copyright ]]]:
131133 sorted_blame = sorted (
132134 get_contributions (include , exclude ),
133135 key = lambda c : (c .path , c .author , c .email )
@@ -147,15 +149,15 @@ def get_copyrights(include: Set[str], exclude: Set[str])\
147149 yield path , copyrights
148150
149151
150- class Contribution (NamedTuple ):
152+ class Contribution (t . NamedTuple ):
151153 author : str
152154 email : str
153155 year : int
154156 path : Path
155157
156158
157- def get_contributions (include : Set [str ], exclude : Set [str ])\
158- -> Iterator [Contribution ]:
159+ def get_contributions (include : t . Set [str ], exclude : t . Set [str ])\
160+ -> t . Iterator [Contribution ]:
159161 for path in get_source_paths (include , exclude ):
160162 blame = Popen (
161163 ['git' , 'blame' , '--line-porcelain' , 'HEAD' , '--' , str (path )],
@@ -186,7 +188,8 @@ def get_contributions(include: Set[str], exclude: Set[str])\
186188 assert blame .returncode == 0
187189
188190
189- def get_source_paths (include : Set [str ], exclude : Set [str ]) -> Iterator [Path ]:
191+ def get_source_paths (include : t .Set [str ], exclude : t .Set [str ])\
192+ -> t .Iterator [Path ]:
190193 ls_tree = Popen (
191194 ['git' , 'ls-tree' , '-r' , '--name-only' , 'HEAD' ],
192195 stdout = PIPE , stderr = DEVNULL , universal_newlines = True )
@@ -203,9 +206,9 @@ def get_source_paths(include: Set[str], exclude: Set[str]) -> Iterator[Path]:
203206 assert ls_tree .returncode == 0
204207
205208
206- class License (NamedTuple ):
207- ident : Optional [str ]
208- text : List [str ]
209+ class License (t . NamedTuple ):
210+ ident : t . Optional [str ]
211+ text : t . List [str ]
209212
210213
211214def get_license (path : Path , * , spdx_prefix : str = SPDX_PREFIX ) -> License :
@@ -253,20 +256,26 @@ class CopyWriter:
253256 '.sql' : '--' ,
254257 }
255258
256- def __init__ (self , license = 'LICENSE.txt' , preamble = (),
257- spdx_prefix = SPDX_PREFIX , copy_prefix = COPYRIGHT_PREFIX ):
259+ def __init__ (self , license : Path = Path ('LICENSE.txt' ),
260+ preamble : t .List [str ]= None ,
261+ spdx_prefix : str = SPDX_PREFIX ,
262+ copy_prefix : str = COPYRIGHT_PREFIX ):
263+ if preamble is None :
264+ preamble = []
258265 self .license = get_license (license , spdx_prefix = spdx_prefix )
259266 self .preamble = preamble
260267 self .spdx_prefix = spdx_prefix
261268 self .copy_prefix = copy_prefix
262269
263270 @classmethod
264- def from_config (cls , config ) :
271+ def from_config (cls , config : Namespace ) -> CopyWriter :
265272 return cls (
266273 config .license , config .preamble ,
267274 config .spdx_prefix , config .copy_prefix )
268275
269- def transform (self , source , copyrights , * , comment_prefix = None ):
276+ def transform (self , source : t .TextIO ,
277+ copyrights : t .List [Copyright ], * ,
278+ comment_prefix : str = None ) -> t .Iterator [str ]:
270279 if comment_prefix is None :
271280 comment_prefix = self .COMMENTS [Path (source .name ).suffix ]
272281 license_start = self .license .text [0 ]
@@ -279,7 +288,7 @@ class CopyWriter:
279288 yield line
280289 empty = False
281290 elif linenum < 3 and (
282- 'set fileencoding=' in line or '-*- coding:' in line ):
291+ 'fileencoding=' in line or '-*- coding:' in line ):
283292 yield line
284293 empty = False
285294 elif line .rstrip () == comment_prefix :
@@ -313,7 +322,8 @@ class CopyWriter:
313322 elif state == 'body' :
314323 yield line
315324
316- def _generate_header (self , copyrights , comment_prefix , empty ):
325+ def _generate_header (self , copyrights : t .Iterable [Copyright ],
326+ comment_prefix : str , empty : bool ) -> t .Iterator [str ]:
317327 if not empty :
318328 yield comment_prefix + '\n '
319329 for line in self .preamble :
@@ -356,7 +366,7 @@ class AtomicReplaceFile:
356366 If ``None`` (the default), the temporary file will be opened in binary
357367 mode. Otherwise, this specifies the encoding to use with text mode.
358368 """
359- def __init__ (self , path : Union [str , Path ], encoding : str = None ):
369+ def __init__ (self , path : t . Union [str , Path ], encoding : str = None ):
360370 if isinstance (path , str ):
361371 path = Path (path )
362372 self ._path = path
0 commit comments