Skip to content

Commit 89319e2

Browse files
committed
Handle MySQL data files correctly #1298
* properly detect these data files as type and as gzip archives * improve definition anddetection of data files in contenttype Signed-off-by: Philippe Ombredanne <pombredanne@nexb.com>
1 parent 285df1b commit 89319e2

6 files changed

Lines changed: 113 additions & 7 deletions

File tree

src/typecode/contenttype.py

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
import os
3030
import fnmatch
3131
import mimetypes as mimetype_python
32-
import logging
3332

33+
import attr
3434
import binaryornot.check
3535

3636
from pdfminer.pdfparser import PDFParser
@@ -42,6 +42,8 @@
4242

4343
from commoncode import fileutils
4444
from commoncode import filetype
45+
from commoncode.datautils import Boolean
46+
from commoncode.datautils import List
4547
from commoncode.system import on_linux
4648

4749
from typecode.pygments_lexers import ClassNotFound as LexerClassNotFound
@@ -50,13 +52,33 @@
5052

5153
from typecode import magic2
5254
from typecode import entropy
55+
from commoncode.datautils import String
5356

5457
"""
5558
Utilities to detect and report the type of a file or path based on its name,
5659
extension and mostly its content.
5760
"""
5861

59-
LOG = logging.getLogger(__name__)
62+
# Tracing flag
63+
TRACE = False
64+
65+
66+
def logger_debug(*args):
67+
pass
68+
69+
70+
if TRACE:
71+
import logging
72+
import sys
73+
74+
logger = logging.getLogger(__name__)
75+
logging.basicConfig(stream=sys.stdout)
76+
logger.setLevel(logging.DEBUG)
77+
78+
def logger_debug(*args):
79+
return logger.debug(' '.join(isinstance(a, unicode) and a or repr(a) for a in args))
80+
81+
6082

6183
data_dir = os.path.join(os.path.dirname(__file__), 'data')
6284

@@ -535,11 +557,13 @@ def is_data(self):
535557
size = self.size
536558
max_entropy = 1.3
537559

538-
if (('data' in ft and size > large_file)
560+
if (ft=='data'
561+
or is_data(self.location)
562+
or ('data' in ft and size > large_file)
539563
or (self.is_text and size > large_text_file)
540564
or (self.is_text and size > large_text_file)
541-
or (entropy.entropy(self.location, length=5000) < max_entropy)
542-
):
565+
or (entropy.entropy(self.location, length=5000) < max_entropy)):
566+
543567
self._is_data = True
544568
else:
545569
self._is_data = False
@@ -664,6 +688,64 @@ def is_java_class(self):
664688
return False
665689

666690

691+
@attr.attributes
692+
class TypeDefinition(object):
693+
name=String(repr=True)
694+
filetypes=List(repr=True)
695+
mimetypes=List(repr=True)
696+
extensions=List(repr=True)
697+
strict=Boolean(repr=True,
698+
help=' if True, all criteria must be matched to select this detector.')
699+
700+
701+
DATA_TYPE_DEFINITIONS =tuple([
702+
TypeDefinition(
703+
name='MySQL ARCHIVE Storage Engine data files',
704+
filetypes=('mysql table definition file',),
705+
extensions=('.arm','.arz', '.arn',),
706+
),
707+
])
708+
709+
710+
def is_data(location, definitions=DATA_TYPE_DEFINITIONS):
711+
"""
712+
Return True isthe file at `location` is a data file.
713+
"""
714+
if on_linux:
715+
location = fileutils.fsencode(location)
716+
717+
if not filetype.is_file(location):
718+
return False
719+
720+
T = get_type(location)
721+
ftype = T.filetype_file.lower()
722+
mtype = T.mimetype_file.lower()
723+
724+
for ddef in definitions:
725+
type_matched = ddef.filetypes and any(t in ftype for t in ddef.filetypes)
726+
mime_matched = ddef.mimetypes and any(m in mtype for m in ddef.mimetypes)
727+
728+
exts = ddef.extensions
729+
if exts:
730+
if on_linux:
731+
exts = tuple(fileutils.fsencode(e) for e in exts)
732+
extension_matched = exts and location.lower().endswith(exts)
733+
734+
if TRACE:
735+
logger_debug('is_data: considering def: %(ddef)r for %(location)s' % locals())
736+
logger_debug('matched type: %(type_matched)s, mime: %(mime_matched)s, ext: %(extension_matched)s' % locals())
737+
738+
if ddef.strict and not all([type_matched, mime_matched, extension_matched]):
739+
continue
740+
741+
if type_matched or mime_matched or extension_matched:
742+
if TRACE:
743+
logger_debug('is_data: True: %(location)s: ' % locals())
744+
return True
745+
746+
return False
747+
748+
667749
def get_pygments_lexer(location):
668750
"""
669751
Given an input file location, return a Pygments lexer appropriate for
612 Bytes
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
157 Bytes
Binary file not shown.

tests/typecode/test_contenttype.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from typecode.contenttype import get_type
4141
from typecode.contenttype import get_pygments_lexer
4242
from typecode.contenttype import is_standard_include
43+
from typecode.contenttype import is_data as contenttype_is_data
4344

4445
# aliases for testing
4546
get_mimetype_python = lambda l: get_type(l).mimetype_python
@@ -1195,12 +1196,33 @@ def test_rgb_stream_is_binary(self):
11951196
assert 'data' == get_filetype_file(test_file)
11961197
assert 'application/octet-stream' == get_mimetype_file(test_file)
11971198
assert is_binary(test_file)
1199+
assert is_data(test_file)
11981200

11991201
def test_large_text_file_is_data(self):
12001202
test_file = self.get_test_loc('contenttype/data/nulls.txt')
12011203
assert is_data(test_file)
12021204
assert '' == get_filetype_pygment(test_file)
12031205

1206+
def test_is_data_for_mysql1(self):
1207+
test_file = self.get_test_loc('contenttype/data/mysql-arch')
1208+
assert contenttype_is_data(test_file)
1209+
assert is_data(test_file)
1210+
1211+
def test_is_data_for_mysql2(self):
1212+
test_file = self.get_test_loc('contenttype/data/mysql-arch.ARM')
1213+
assert contenttype_is_data(test_file)
1214+
assert is_data(test_file)
1215+
1216+
def test_is_data_for_mysql3(self):
1217+
test_file = self.get_test_loc('contenttype/data/mysql-arch.ARN')
1218+
assert contenttype_is_data(test_file)
1219+
assert is_data(test_file)
1220+
1221+
def test_is_data_for_mysql4(self):
1222+
test_file = self.get_test_loc('contenttype/data/mysql-arch.ARZ')
1223+
assert contenttype_is_data(test_file)
1224+
assert is_data(test_file)
1225+
12041226
def test_is_js_map_for_css(self):
12051227
test_file = self.get_test_loc('contenttype/build/ar-ER.css.map')
12061228
assert is_js_map(test_file)
@@ -1211,12 +1233,12 @@ def test_is_js_map_for_js(self):
12111233
assert is_js_map(test_file)
12121234
assert '' == get_filetype_pygment(test_file)
12131235

1214-
def test_test_is_js_map_for_binary(self):
1236+
def test_is_js_map_for_binary(self):
12151237
test_file = self.get_test_loc('contenttype/build/binary.js.map')
12161238
assert not is_js_map(test_file)
12171239
assert '' == get_filetype_pygment(test_file)
12181240

1219-
def test_test_is_js_map_for_makefile(self):
1241+
def test_is_js_map_for_makefile(self):
12201242
test_file = self.get_test_loc('contenttype/build/Makefile')
12211243
assert not is_js_map(test_file)
12221244
assert '' == get_filetype_pygment(test_file)

0 commit comments

Comments
 (0)