11#!/usr/bin/env python3
2- # coding:utf8
2+ # -*- coding: utf-8 -*-
3+
34import os
4- import re
55from collections import defaultdict
6+ from code_counter .conf .config import Config
7+ from code_counter .core .countable .iterator import CountableFileIterator
68
79
810class CodeCounter :
911
10- def __init__ (self , config ):
12+ def __init__ (self ):
13+ self .config = Config ()
14+
1115 self .total_file_lines = 0
1216 self .total_code_lines = 0
1317 self .total_blank_lines = 0
1418 self .total_comment_lines = 0
1519 self .files_of_language = defaultdict (int )
16- self .config = config
1720
1821 self .search_args = None
19- self .comment_symbol = ()
20- self .ignore = ()
2122 self .lines_of_language = {}
22- self .pattern = None
2323
2424 self .result = {
2525 'total' : {
@@ -33,42 +33,59 @@ def __init__(self, config):
3333
3434 def setSearchArgs (self , args ):
3535 self .search_args = args
36+ if args .suffix :
37+ self .config .suffix = set (args .suffix )
38+ if args .comment :
39+ self .config .comment = set (args .comment )
40+ if args .ignore :
41+ self .config .ignore = set (args .ignore )
3642
37- suffix = args .suffix if args .suffix else self .config .suffix
38- self .comment_symbol = tuple (args .comment if args .comment else self .config .comment )
39- self .ignore = tuple (args .ignore if args .ignore else self .config .ignore )
40-
41- self .lines_of_language = {suffix : 0 for suffix in suffix }
42-
43- regex = '.*\.({})$' .format ('|' .join (suffix ))
44- self .pattern = re .compile (regex )
43+ self .lines_of_language = {suffix : 0 for suffix in self .config .suffix }
4544
4645 def search (self ):
47- if not self .search_args :
48- raise Exception ('search_args is None, please invoke setSearchArgs first.' )
46+ if self .search_args is None :
47+ raise Exception ('search_args is None, please invoke the ` setSearchArgs` function first.' )
4948
5049 input_path = self .search_args .input_path
51- output_path = self .search_args .output_path
50+ if not input_path :
51+ print ('{} is not a validate path.' .format (input_path ))
52+ return
5253
54+ output_path = self .search_args .output_path
5355 output_file = open (output_path , 'w' ) if output_path else None
5456
5557 if self .search_args .verbose :
56- print ('\n \t {}' .format ("SEARCHING" ), file = output_file )
57- print ("\t {}" .format ('=' * 20 ), file = output_file )
58- print ('\t {:>10} |{:>10} |{:>10} |{:>10} |{:>10} | {}'
59- .format ("File Type" , "Lines" , "Code" , "Blank" , "Comment" , "File Path" ), file = output_file )
60- print ("\t {}" .format ('-' * 90 ), file = output_file )
61-
62- if not input_path :
63- print ('{} is not a validate path.' .format (input_path ))
64- return
58+ self .print_searching_verbose_info (output_file )
6559
6660 for path in input_path :
6761 if os .path .exists (path ):
68- self .__search (path , output_file )
62+ for cf in CountableFileIterator (path ):
63+ cf .count ()
64+ if self .search_args .verbose :
65+ print (cf , file = output_file )
66+ self .files_of_language [cf .file_type ] += 1
67+ self .total_file_lines += cf .file_lines
68+ self .total_code_lines += cf .code_lines
69+ self .total_blank_lines += cf .blank_lines
70+ self .total_comment_lines += cf .comment_lines
71+ self .lines_of_language [cf .file_type ] += cf .code_lines
6972 else :
7073 print ('{} is not a validate path.' .format (path ))
74+ exit (1 )
75+
76+ self .print_result_info (output_file )
77+
78+ if output_file :
79+ output_file .close ()
80+
81+ def print_searching_verbose_info (self , output_file = None ):
82+ print ('\n \t {}' .format ("SEARCHING" ), file = output_file )
83+ print ("\t {}" .format ('=' * 20 ), file = output_file )
84+ print ('\t {:>10} |{:>10} |{:>10} |{:>10} |{:>10} | {}'
85+ .format ("File Type" , "Lines" , "Code" , "Blank" , "Comment" , "File Path" ), file = output_file )
86+ print ("\t {}" .format ('-' * 90 ), file = output_file )
7187
88+ def print_result_info (self , output_file = None ):
7289 print ('\n \t {}' .format ("RESULT" ), file = output_file )
7390 print ("\t {}" .format ('=' * 20 ), file = output_file )
7491 print ("\t {:<20}:{:>8} ({:>7})"
@@ -106,106 +123,11 @@ def search(self):
106123
107124 for tp , cnt in self .files_of_language .items ():
108125 code_line = self .lines_of_language [tp ]
126+ self .result ['code' ][tp ] = code_line
127+ self .result ['file' ][tp ] = cnt
109128 print ("\t {:>10} |{:>10} |{:>10} |{:>10} |{:>10}" .format (
110129 tp , cnt , '%.2f%%' % (cnt / total_files * 100 ),
111130 code_line , '%.2f%%' % (code_line / self .total_code_lines * 100 )), file = output_file )
112- self .result ['code' ][tp ] = code_line
113- self .result ['file' ][tp ] = cnt
114-
115- if output_file :
116- output_file .close ()
117-
118- def __search (self , input_path , output_file = None ):
119- """
120- :param input_path: input file path
121- :param output_file: output file path
122- :return:
123- """
124- if os .path .isdir (input_path ):
125- files = os .listdir (input_path )
126- for file in files :
127- file_path = os .path .join (input_path , file )
128- if os .path .isdir (file_path ):
129- if os .path .split (file_path )[- 1 ] in self .ignore :
130- continue
131- self .__search (file_path , output_file )
132- elif os .path :
133- file_path = os .path .join (input_path , file )
134- self .__format_output (file_path , output_file )
135- elif os .path .isfile (input_path ):
136- self .__format_output (input_path , output_file )
137-
138- def __format_output (self , file_path , output_file = None ):
139- """
140- :param file_path: input file path
141- :param output_file: output file path
142- :return:
143- """
144- try :
145- res = re .match (self .pattern , file_path )
146- if res :
147- single = self .count_single (file_path , output_file )
148- file_lines = single ['file_lines' ]
149- code_lines = single ['code_lines' ]
150- blank_lines = single ['blank_lines' ]
151- comment_lines = single ['comment_lines' ]
152-
153- file_type = os .path .splitext (file_path )[1 ][1 :]
154- self .files_of_language [file_type ] += 1
155-
156- if self .search_args .verbose :
157- print ('\t {:>10} |{:>10} |{:>10} |{:>10} |{:>10} | {}'
158- .format (file_type , file_lines , code_lines , blank_lines , comment_lines , file_path ),
159- file = output_file )
160-
161- self .total_file_lines += file_lines
162- self .total_code_lines += code_lines
163- self .total_blank_lines += blank_lines
164- self .total_comment_lines += comment_lines
165- self .lines_of_language [file_type ] += code_lines
166- except AttributeError as e :
167- print (e )
168-
169- def count_single (self , file_path , output_file = None ):
170- """
171- :param file_path: the file you want to count
172- :param output_file: output file path
173- :return: single { file_lines, code_lines, blank_lines, comment_lines }
174- """
175- assert os .path .isfile (file_path ), "Function: 'code_counter' need a file path, but {} is not." .format (file_path )
176-
177- single = {
178- 'file_lines' : 0 ,
179- 'code_lines' : 0 ,
180- 'blank_lines' : 0 ,
181- 'comment_lines' : 0 ,
182- }
183-
184- with open (file_path , 'rb' ) as handle :
185- for line_number , raw_line in enumerate (handle ):
186- try :
187- line = raw_line .strip ().decode ('utf8' )
188- except UnicodeDecodeError :
189- try :
190- # If the code line contain Chinese string, decode it as gbk
191- line = raw_line .strip ().decode ('gbk' )
192- except UnicodeDecodeError :
193- if self .search_args .verbose :
194- print ('\n \t {:>10} | decode line occurs a problem, non-count it, at File "{}", line {}:'
195- .format ('WARN' , file_path , line_number ),
196- file = output_file )
197- print ('\t {:>10} | {}\n '
198- .format (' ' , raw_line ))
199- continue
200-
201- single ['file_lines' ] += 1
202- if not line :
203- single ['blank_lines' ] += 1
204- elif line .startswith (self .comment_symbol ):
205- single ['comment_lines' ] += 1
206- else :
207- single ['code_lines' ] += 1
208- return single
209131
210132 def visualize (self ):
211133 from matplotlib import pyplot as plt
0 commit comments