Skip to content

Commit 190554b

Browse files
committed
Improve licenses detection accuracy of unknowns using ngrams
Signed-off-by: akugarg <akanksha.garg2k@gmail.com>
1 parent f9601df commit 190554b

2 files changed

Lines changed: 90 additions & 4 deletions

File tree

src/licensedcode/index.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@
1919

2020
from intbitset import intbitset
2121

22-
from licensedcode import SMALL_RULE
22+
from licensedcode import SMALL_RULE, match_unknown
2323
from licensedcode.legalese import common_license_words
2424
from licensedcode import match
2525
from licensedcode import match_aho
2626
from licensedcode import match_hash
2727
from licensedcode import match_seq
2828
from licensedcode import match_set
2929
from licensedcode import match_spdx_lid
30+
from licensedcode import match_unknown
3031
from licensedcode.dmp import match_blocks as match_blocks_dmp
3132
from licensedcode.seq import match_blocks as match_blocks_seq
3233
from licensedcode import query
@@ -128,6 +129,7 @@ class LicenseIndex(object):
128129
'rules_automaton',
129130
'fragments_automaton',
130131
'starts_automaton',
132+
'unknown_ngrams',
131133

132134
'regular_rids',
133135
'false_positive_rids',
@@ -136,7 +138,7 @@ class LicenseIndex(object):
136138
'optimized',
137139
)
138140

139-
def __init__(self, rules=None, _legalese=common_license_words, _spdx_tokens=frozenset()):
141+
def __init__(self, rules=None, _legalese=common_license_words, _spdx_tokens=frozenset(), _unknown_ngram_length=7):
140142
"""
141143
Initialize the index with an iterable of Rule objects.
142144
`_legalese` is a set of common license-specific words aka. legalese
@@ -185,6 +187,7 @@ def __init__(self, rules=None, _legalese=common_license_words, _spdx_tokens=froz
185187
self.rules_automaton = match_aho.get_automaton()
186188
self.fragments_automaton = USE_AHO_FRAGMENTS and match_aho.get_automaton()
187189
self.starts_automaton = USE_RULE_STARTS and match_aho.get_automaton()
190+
self.unknown_ngrams = match_aho.get_automaton()
188191

189192
# disjunctive sets of rule ids: regular and false positive
190193

@@ -206,7 +209,7 @@ def __init__(self, rules=None, _legalese=common_license_words, _spdx_tokens=froz
206209
logger_debug('LicenseIndex: building index.')
207210
# index all and optimize
208211
self._add_rules(
209-
rules, _legalese=_legalese, _spdx_tokens=_spdx_tokens)
212+
rules, _legalese=_legalese, _spdx_tokens=_spdx_tokens, , _unknown_ngram_length=_unknown_ngram_length)
210213

211214
if TRACE_TOKEN_DOC_FREQ:
212215
logger_debug('LicenseIndex: token, frequency')
@@ -222,7 +225,7 @@ def __init__(self, rules=None, _legalese=common_license_words, _spdx_tokens=froz
222225
'%(duration)f seconds.' % locals())
223226
self._print_index_stats()
224227

225-
def _add_rules(self, rules, _legalese=common_license_words, _spdx_tokens=frozenset()):
228+
def _add_rules(self, rules, _legalese=common_license_words, _spdx_tokens=frozenset(), _unknown_ngram_length=7):
226229
"""
227230
Add a list of Rule objects to the index and constructs optimized and
228231
immutable index structures.
@@ -358,6 +361,12 @@ def _add_rules(self, rules, _legalese=common_license_words, _spdx_tokens=frozens
358361
rid_by_hash[rule_hash] = rid
359362
regular_rids_add(rid)
360363

364+
match_unknown.add_ngrams(
365+
automaton=self.unknown_ngrams,
366+
tids=rule_token_ids,
367+
rule_length=rule.length,
368+
unknown_ngram_length=_unknown_ngram_length,
369+
)
361370
# Some rules cannot be matched as a sequence are "weak" rules
362371
if not is_weak:
363372
approx_matchable_rids_add(rid)

src/licensedcode/match_unknown.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# ScanCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/nexB/scancode-toolkit for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
from licensedcode import tokenize
11+
"""
12+
Matching strategy for unknown matching using ngrams.
13+
"""
14+
15+
# Set to False to enable debug tracing
16+
TRACE = False
17+
18+
if TRACE:
19+
import logging
20+
import sys
21+
22+
logger = logging.getLogger(__name__)
23+
24+
def logger_debug(*args):
25+
return logger.debug(' '.join(isinstance(a, str) and a or repr(a) for a in args))
26+
27+
logging.basicConfig(stream=sys.stdout)
28+
logger.setLevel(logging.DEBUG)
29+
30+
else:
31+
32+
def logger_debug(*args):
33+
pass
34+
35+
MATCH_UNKNOWN = '6-unknown'
36+
37+
38+
def add_ngrams(automaton, tids, rule_length, unknown_ngram_length=7):
39+
"""
40+
Add the `tids` sequence of token ids to an unknown ngram automaton.
41+
"""
42+
if rule_length < unknown_ngram_length:
43+
return
44+
45+
rule_ngrams = tokenize.ngrams(tids, ngram_length=unknown_ngram_length)
46+
47+
for ngram in rule_ngrams:
48+
ngram = tuple(ngram)
49+
automaton.add_word(ngram, ngram)
50+
51+
52+
def unknown_match(idx, query_run, automaton, unknown_ngram_length=7, **kwargs):
53+
"""
54+
Return a list of unknown LicenseMatch by matching the `query_run` against
55+
the `automaton` and `idx` index.
56+
"""
57+
matches = list(get_matches(
58+
qtokens=query_run.tokens,
59+
qbegin=query_run.start,
60+
automaton=automaton,
61+
unknown_ngram_length=unknown_ngram_length,
62+
))
63+
return matches
64+
65+
66+
def get_matches(qtokens, qbegin, automaton, unknown_ngram_length=7):
67+
"""
68+
Yield tuples of automaton matches positions as (match start, match end, match value) from
69+
matching `qtokens` sequence of query token ids starting at the `qbegin` absolute
70+
query start position position using the `automaton`.
71+
"""
72+
# iterate over matched strings: the matched value is matching ngram
73+
qtokens = tuple(qtokens)
74+
for qend, matched_ngram in automaton.iter(qtokens):
75+
qend = qbegin + qend + 1
76+
qstart = qend - unknown_ngram_length
77+
yield qstart, qend, matched_ngram

0 commit comments

Comments
 (0)