Skip to content

Commit de2e0d0

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

3 files changed

Lines changed: 64 additions & 4 deletions

File tree

src/licensedcode/index.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020
from intbitset import intbitset
2121

22-
from licensedcode import SMALL_RULE, match_unknown
22+
from licensedcode import SMALL_RULE
23+
from licensedcode import match_unknown
2324
from licensedcode.legalese import common_license_words
2425
from licensedcode import match
2526
from licensedcode import match_aho
@@ -32,6 +33,7 @@
3233
from licensedcode.seq import match_blocks as match_blocks_seq
3334
from licensedcode import query
3435
from licensedcode import tokenize
36+
from licensedcode.spans import Span
3537

3638
"""
3739
Main license index construction, query processing and matching entry points for
@@ -883,6 +885,25 @@ def match(
883885
# break if deadline has passed
884886
if time() > deadline:
885887
break
888+
889+
# refining matches without filtering false positives
890+
matches, _discarded = match.refine_matches(
891+
matches=matches,
892+
idx=self,
893+
query=qry,
894+
min_score=min_score,
895+
filter_false_positive=False,
896+
merge=True,
897+
)
898+
899+
original_qspan = Span(0, len(qry.tokens)-1)
900+
matched_qspans = [m.qspan for m in matches]
901+
matched_qspan = Span()
902+
matched_qspan.union(*matched_qspans)
903+
unmatched_qspan = original_qspan.difference(matched_qspan)
904+
905+
for subspan in unmatched_qspan.subspans():
906+
query_run = query.QueryRun(query=qry, start=subspan.start, end=subspan.end)
886907

887908
if not matches:
888909
return []

src/licensedcode/match_unknown.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#
99

1010
from licensedcode import tokenize
11+
from licensedcode.match import LicenseMatch
12+
from licensedcode.models import UnknownRule
13+
from licensedcode.spans import Span
1114
"""
1215
Matching strategy for unknown matching using ngrams.
1316
"""
@@ -49,17 +52,23 @@ def add_ngrams(automaton, tids, rule_length, unknown_ngram_length=7):
4952
automaton.add_word(ngram, ngram)
5053

5154

52-
def unknown_match(idx, query_run, automaton, unknown_ngram_length=7, **kwargs):
55+
def match_unknowns(idx, query_run, automaton, unknown_ngram_length=7, **kwargs):
5356
"""
5457
Return a list of unknown LicenseMatch by matching the `query_run` against
5558
the `automaton` and `idx` index.
5659
"""
57-
matches = list(get_matches(
60+
matches = get_matches(
5861
qtokens=query_run.tokens,
5962
qbegin=query_run.start,
6063
automaton=automaton,
6164
unknown_ngram_length=unknown_ngram_length,
62-
))
65+
)
66+
67+
qspans = (Span(qstart, qend) for qstart, qend, matched_ngram in matches)
68+
qspan = Span().union(*qspans)
69+
ispan = Span(0, len(qspan))
70+
rule = UnknownRule()
71+
6372
return matches
6473

6574

src/licensedcode/models.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,6 +1473,36 @@ def dump(self):
14731473
raise NotImplementedError
14741474

14751475

1476+
@attr.s(slots=True, repr=False)
1477+
class UnknownRule(Rule):
1478+
"""
1479+
A specialized rule object that is used for the special case of unknown license
1480+
detection.
1481+
Since we may have an infinite possible number of unknown licenses and these
1482+
are not backed by a traditional rule text file, we use this class to handle
1483+
the specifics of these how rules are built at matching time: one rule
1484+
is created for each detected unknown license.
1485+
"""
1486+
1487+
def __attrs_post_init__(self, *args, **kwargs):
1488+
self.identifier = f'unknown-license-identifier: '
1489+
self.license_expression = 'unknown-license'
1490+
expression = self.licensing.parse(self.license_expression)
1491+
1492+
self.is_unknown = True
1493+
self.license_expression_object = expression
1494+
self.is_license_notice = True
1495+
self.is_small = False
1496+
self.relevance = 100
1497+
self.has_stored_relevance = True
1498+
1499+
def load(self):
1500+
raise NotImplementedError
1501+
1502+
def dump(self):
1503+
raise NotImplementedError
1504+
1505+
14761506
def _print_rule_stats():
14771507
"""
14781508
Print rules statistics.

0 commit comments

Comments
 (0)