@@ -106,6 +106,30 @@ def as_bytes(items):
106106ELF_UNKNOWN = 'unknown'
107107elf_types = (ELF_EXE , ELF_SHARED , ELF_RELOC ,)
108108
109+
110+ PLAIN_TEXT_EXTENSIONS = (
111+ # docs
112+ '.rst' , '.rest' , '.md' ,
113+ '.txt' ,
114+ # This one is actually not handled by Pygments. There are probably more.
115+ '.log' ,
116+ # various data
117+ '.json' ,
118+ '.xml' ,
119+ )
120+
121+ if on_linux and py2 :
122+ PLAIN_TEXT_EXTENSIONS = as_bytes (PLAIN_TEXT_EXTENSIONS )
123+
124+
125+ MAKEFILE_EXTENSIONS = (
126+ 'Makefile' ,
127+ 'Makefile.inc' ,
128+ )
129+
130+ if on_linux and py2 :
131+ MAKEFILE_EXTENSIONS = as_bytes (MAKEFILE_EXTENSIONS )
132+
109133# TODO:
110134# http://svn.zope.org/z3c.mimetype/trunk/?pathrev=103648
111135# http://svn.zope.org/z3c.sharedmimeinfo/trunk/TODO.txt?revision=103668&view=markup
@@ -157,7 +181,7 @@ class Type(object):
157181 '_mimetype_python' ,
158182 '_filetype_file' ,
159183 '_mimetype_file' ,
160- '_filetype_pygments ' ,
184+ '_filetype_pygment ' ,
161185 '_is_pdf_with_text' ,
162186 '_is_text' ,
163187 '_is_text_with_long_lines' ,
@@ -178,18 +202,19 @@ class Type(object):
178202 'filetype_pygment' ,
179203 'elf_type' ,
180204 'programming_language' ,
205+ 'link_target' ,
181206 ]
182207
183- exportable_attributes = text_attributes + [
208+ numeric_attributes = ['size' , ]
209+ date_attributes = ['date' , ]
210+
211+ boolean_attributes = [
184212 'is_file' ,
185213 'is_dir' ,
186214 'is_regular' ,
187215 'is_special' ,
188- 'date' ,
189216 'is_link' ,
190217 'is_broken_link' ,
191- 'link_target' ,
192- 'size' ,
193218 'is_pdf_with_text' ,
194219 'is_text' ,
195220 'is_text_with_long_lines' ,
@@ -216,7 +241,10 @@ class Type(object):
216241 'is_source' ,
217242 'is_stripped_elf' ,
218243 'is_winexe' ,
244+ 'is_makefile' ,
219245 ]
246+ exportable_attributes = (
247+ text_attributes + numeric_attributes + date_attributes + boolean_attributes )
220248
221249 def __init__ (self , location ):
222250 if (not location
@@ -234,7 +262,7 @@ def __init__(self, location):
234262 self .date = filetype .get_last_modified_date (location )
235263
236264 self .is_link = filetype .is_link (location )
237- self .is_broken_link = filetype .is_broken_link (location )
265+ self .is_broken_link = bool ( filetype .is_broken_link (location ) )
238266
239267 # FIXME: the way the True and False values are checked in properties is verbose and contrived at best
240268 # and is due to use None/True/False as different values
@@ -245,7 +273,7 @@ def __init__(self, location):
245273 self ._mimetype_python = None
246274 self ._filetype_file = None
247275 self ._mimetype_file = None
248- self ._filetype_pygments = None
276+ self ._filetype_pygment = None
249277 self ._is_pdf_with_text = None
250278 self ._is_text = None
251279 self ._is_text_with_long_lines = None
@@ -267,7 +295,8 @@ def to_dict(self, include_date=True):
267295 """
268296 nv = ((n , getattr (self , n )) for n in self .exportable_attributes )
269297 if not include_date :
270- nv = ((n ,v ) for n ,v in nv if n != 'date' )
298+ nv = ((n , v ) for n , v in nv if n not in self .date_attributes )
299+
271300 return OrderedDict (nv )
272301
273302 @property
@@ -303,8 +332,8 @@ def mimetype_python(self):
303332 if self .is_file is True :
304333 if not mimetype_python .inited :
305334 mimetype_python .init ([APACHE_MIME_TYPES ])
306- val = mimetype_python .guess_type (self .location )[0 ]
307- self ._mimetype_python = val
335+ val = mimetype_python .guess_type (self .location )[0 ]
336+ self ._mimetype_python = val or ''
308337 return self ._mimetype_python
309338
310339 @property
@@ -334,15 +363,15 @@ def filetype_pygment(self):
334363 """
335364 Return the filetype guessed using Pygments lexer, mostly for source code.
336365 """
337- if self ._filetype_pygments is None :
338- self ._filetype_pygments = ''
366+ if self ._filetype_pygment is None :
367+ self ._filetype_pygment = ''
339368 if self .is_text and not self .is_media :
340369 lexer = get_pygments_lexer (self .location )
341370 if lexer and not lexer .name .startswith ('JSON' ):
342- self ._filetype_pygments = lexer .name or ''
371+ self ._filetype_pygment = lexer .name or ''
343372 else :
344- self ._filetype_pygments = ''
345- return self ._filetype_pygments
373+ self ._filetype_pygment = ''
374+ return self ._filetype_pygment
346375
347376 # FIXME: we way we use tri booleans is a tad ugly
348377
@@ -585,8 +614,11 @@ def is_media_with_meta(self):
585614 # but we exclude some.
586615
587616 # FIXME: only include types that are known to have metadata
588- if (self .is_media and self .filetype_file .lower ().startswith (
589- ('gif image' , 'png image' , 'jpeg image' , 'netpbm' , 'mpeg' ))):
617+ if not self .is_media :
618+ return False
619+ if self .filetype_file .lower ().startswith (
620+ ('gif image' , 'png image' , 'jpeg image' , 'netpbm' , 'mpeg' )
621+ ):
590622 return False
591623 else :
592624 return True
@@ -596,8 +628,7 @@ def is_pdf(self):
596628 """
597629 Return True if the file is highly likely to be a pdf file.
598630 """
599- ft = self .mimetype_file
600- if 'pdf' in ft :
631+ if 'pdf' in self .mimetype_file :
601632 return True
602633 else :
603634 return False
@@ -699,25 +730,33 @@ def is_script(self):
699730 else :
700731 return False
701732
733+ def _is_plain_text (self , _pte = PLAIN_TEXT_EXTENSIONS ):
734+ return self .location .endswith (_pte )
735+
736+ @property
737+ def is_makefile (self ):
738+ return self .location .endswith (MAKEFILE_EXTENSIONS )
739+
702740 @property
703741 def is_source (self ):
704742 """
705743 Return True if the file is source code.
706744 """
707745 if self .is_text is False :
708746 return False
709- PLAIN_TEXT_EXTENSIONS = (
710- '.rst' , '.rest' , '.txt' , '.md' ,
711- # This one is actually not handled by Pygments. There are probably more.
712- '.log' )
713- if on_linux and py2 :
714- PLAIN_TEXT_EXTENSIONS = as_bytes (PLAIN_TEXT_EXTENSIONS )
715747
716- if self .location .endswith (PLAIN_TEXT_EXTENSIONS ):
748+ elif self ._is_plain_text ():
749+ return False
750+
751+ elif self .is_makefile or self .is_js_map :
717752 return False
718753
719- if self .filetype_pygment or self .is_script is True :
754+ elif self .is_java_source is True or self .is_c_source is True :
755+ return True
756+
757+ elif self .filetype_pygment or self .is_script is True :
720758 return True
759+
721760 else :
722761 return False
723762
@@ -727,7 +766,9 @@ def programming_language(self):
727766 Return the programming language if the file is source code or an empty
728767 string.
729768 """
730- return self .filetype_pygment or ''
769+ if self .is_source :
770+ return self .filetype_pygment or ''
771+ return ''
731772
732773 @property
733774 def is_c_source (self ):
@@ -797,6 +838,7 @@ def is_java_source(self):
797838
798839 if (fnmatch .fnmatch (name , b'*.java' if on_linux and py2 else u'*.java' )
799840 or fnmatch .fnmatch (name , b'*.aj' if on_linux and py2 else u'*.aj' )
841+ or fnmatch .fnmatch (name , b'*.jad' if on_linux and py2 else u'*.jad' )
800842 or fnmatch .fnmatch (name , b'*.ajt' if on_linux and py2 else u'*.ajt' )):
801843 return True
802844 else :
@@ -889,31 +931,43 @@ def get_pygments_lexer(location):
889931 except KeyError :
890932 if is_binary (location ):
891933 return
892- try :
893- # FIXME: Latest Pygments versions should work fine
894- # win32_bug_on_s_files = dejacode.on_windows and location.endswith('.s')
895934
896- # NOTE: we use only the location for its file name here, we could use
897- # lowercase location may be
898- lexer = get_lexer_for_filename (location , stripnl = False , stripall = False )
899- return lexer
935+ # We first try to get a lexer using
936+ # - the filename
937+ # - then the lowercased filename
938+ # - and finally the begining of the file content.
939+ # We try with lowercase as detection is skewed otherwise (e.g. .java vs .JAVA)
900940
941+ try :
942+ return get_lexer_for_filename (location )
901943 except LexerClassNotFound :
902944 try :
903- # if Pygments does not guess we should not carry forward
904- # read the first 4K of the file
905- try :
906- with io .open (location , 'r' ) as f :
907- content = f .read (4096 )
908- except :
909- # try again as bytes and force unicode
910- with open (location , 'rb' ) as f :
911- content = text .as_unicode (f .read (4096 ))
912-
913- guessed = guess_lexer (content )
914- return guessed
945+ return get_lexer_for_filename (location .lower ())
915946 except LexerClassNotFound :
916- return
947+ try :
948+ # if Pygments does not guess we should not carry forward
949+ content = get_text_file_start (location )
950+ return guess_lexer (content )
951+ except LexerClassNotFound :
952+ return
953+
954+
955+ def get_text_file_start (location , length = 4096 ):
956+ """
957+ Return a unicode string with up the first "length" characters from the text
958+ file at location.
959+ """
960+ content = None
961+ # read the first 4K of the file
962+ try :
963+ with io .open (location , 'r' ) as f :
964+ content = f .read (length )
965+ except :
966+ # try again as bytes and force unicode
967+ with open (location , 'rb' ) as f :
968+ content = text .as_unicode (f .read (length ))
969+ finally :
970+ return content
917971
918972
919973def get_filetype (location ):
0 commit comments