Skip to content

Commit 8b02c5a

Browse files
committed
Virtual Codebase Integration
Signed-off-by: Pratik Dey <pratikrocks.dey11@gmail.com>
1 parent 7bf8176 commit 8b02c5a

94 files changed

Lines changed: 2720 additions & 1830 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ include_package_data = true
3131
zip_safe = false
3232
install_requires =
3333
bitarray==1.1.0
34-
commoncode>=21.5.12
34+
commoncode>=21.6.11
3535
click
3636
simplejson
3737
unicodecsv

src/deltacode/__init__.py

Lines changed: 88 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@
2828
import os
2929
from collections import OrderedDict
3030

31-
from deltacode.models import File
32-
from deltacode.models import Scan
3331
from deltacode import utils
32+
from deltacode.exceptions import FileError as FileError
3433
from commoncode import paths
3534
from commoncode.resource import VirtualCodebase
3635

36+
3737
from pkg_resources import get_distribution, DistributionNotFound
3838

3939
try:
@@ -55,38 +55,35 @@ class DeltaCode(object):
5555
def __init__(self, new_path, old_path, options):
5656
self.codebase1 = None
5757
self.codebase2 = None
58-
self.new_files_fingerprint = (
59-
dict()
60-
) # map of { {new_file1:fingerprint},{new_file2:fingerprint},...} it will be needed when we need the fingerprints
61-
self.old_files_fingerprint = (
62-
dict()
63-
) # map of { {old_file1:fingerprint},{old_file2:fingerprint},...}
6458
self.options = options
6559
self.deltas = []
6660
self.errors = []
6761

68-
try:
62+
if os.path.isfile(new_path) and os.path.isfile(old_path):
6963
self.codebase1 = VirtualCodebase(new_path)
7064
self.codebase2 = VirtualCodebase(old_path)
7165

72-
except Exception as exception:
73-
self.errors.append(str(exception))
74-
75-
if self.codebase1 is not None or self.codebase2 is not None:
76-
self.stats = Stat(
77-
self.codebase1.compute_counts(), self.codebase2.compute_counts()
66+
else:
67+
error_message = (
68+
"{} is expected to be a file".format(new_path)
69+
if not os.path.isfile(new_path)
70+
else "{} is expected to be a file".format(old_path)
7871
)
79-
self.new_files_errors = []
80-
self.old_files_errors = []
81-
self.determine_delta()
82-
self.license_diff()
83-
self.copyright_diff()
84-
self.stats.calculate_stats()
85-
self.similarity()
86-
# Sort deltas by score, descending, i.e., high > low, and then by
87-
# factors, alphabetically. Run the least significant sort first.
88-
self.deltas.sort(key=lambda Delta: Delta.factors, reverse=False)
89-
self.deltas.sort(key=lambda Delta: Delta.score, reverse=True)
72+
raise FileError(error_message)
73+
self.stats = Stat(
74+
self.codebase1.compute_counts(), self.codebase2.compute_counts()
75+
)
76+
self.new_files_errors = []
77+
self.old_files_errors = []
78+
self.determine_delta()
79+
self.license_diff()
80+
self.copyright_diff()
81+
self.stats.calculate_stats()
82+
self.similarity()
83+
# Sort deltas by score, descending, i.e., high > low, and then by
84+
# factors, alphabetically. Run the least significant sort first.
85+
self.deltas.sort(key=lambda Delta: Delta.factors, reverse=False)
86+
self.deltas.sort(key=lambda Delta: Delta.score, reverse=True)
9087

9188
def similarity(self):
9289
"""
@@ -99,17 +96,21 @@ def similarity(self):
9996
for delta in self.deltas:
10097
if delta.new_file == None or delta.old_file == None:
10198
continue
102-
new_fingerprint = self.new_files_fingerprint.get(delta.new_file.path, None)
103-
old_fingerprint = self.old_files_fingerprint.get(delta.old_file.path, None)
99+
new_fingerprint = (
100+
delta.new_file.fingerprint
101+
if hasattr(delta.new_file, "fingerprint")
102+
else None
103+
)
104+
old_fingerprint = (
105+
delta.old_file.fingerprint
106+
if hasattr(delta.old_file, "fingerprint")
107+
else None
108+
)
104109

105110
if new_fingerprint == None or old_fingerprint == None:
106111
continue
107-
new_fingerprint = utils.bitarray_from_hex(
108-
self.new_files_fingerprint[delta.new_file.path]
109-
)
110-
old_fingerprint = utils.bitarray_from_hex(
111-
self.old_files_fingerprint[delta.old_file.path]
112-
)
112+
new_fingerprint = utils.bitarray_from_hex(delta.new_file.fingerprint)
113+
old_fingerprint = utils.bitarray_from_hex(delta.old_file.fingerprint)
113114

114115
hamming_distance = utils.hamming_distance(new_fingerprint, old_fingerprint)
115116
if hamming_distance > 0 and hamming_distance <= SIMILARITY_LIMIT:
@@ -119,13 +120,8 @@ def similarity(self):
119120
)
120121

121122
def create_deltas(
122-
self, new_resource, old_resource, new_path, old_path, score, count, status
123+
self, new_resource, old_resource, new_path, old_path, score, status
123124
):
124-
count = count + 1
125-
if new_resource:
126-
new_resource.path = new_path
127-
if old_resource:
128-
old_resource.path = old_path
129125
delta = Delta(score, new_resource, old_resource)
130126
delta.status = status
131127
self.deltas.append(delta)
@@ -137,105 +133,107 @@ def determine_delta(self):
137133
from either scan.
138134
"""
139135

140-
old_files_sha1_considered_in_deltas = dict()
141-
142-
Delta.NEW_CODEBASE_OFFSET, Delta.OLD_CODEBASE_OFFSET = utils.align_trees(
143-
self.codebase1, self.codebase2
144-
)
136+
old_resource_considered = dict()
137+
try:
138+
Delta.NEW_CODEBASE_OFFSET, Delta.OLD_CODEBASE_OFFSET = utils.align_trees(
139+
self.codebase1, self.codebase2
140+
)
141+
except utils.AlignmentException:
142+
Delta.NEW_CODEBASE_OFFSET, Delta.OLD_CODEBASE_OFFSET = 0, 0
145143

146144
for new_resource in self.codebase1.walk():
147145
if new_resource.is_file:
148146
path_new = "/".join(
149147
paths.split(new_resource.path)[Delta.NEW_CODEBASE_OFFSET :]
150148
)
149+
150+
old_resource = self.codebase2.get_resource_from_path(path_new)
151+
152+
if old_resource and old_resource.sha1 == new_resource.sha1:
153+
old_resource_considered[old_resource.path] = 1
154+
path_old = "/".join(
155+
paths.split(old_resource.path)[Delta.OLD_CODEBASE_OFFSET :]
156+
)
157+
self.create_deltas(
158+
new_resource, old_resource, path_new, path_old, 0, "unmodified",
159+
)
160+
self.stats.num_unmodified += 1
161+
162+
continue
163+
151164
ADDED = True
152165
for old_resource in self.codebase2.walk():
166+
if old_resource.path in old_resource_considered.keys():
167+
continue
168+
path_old = "/".join(
169+
paths.split(old_resource.path)[Delta.OLD_CODEBASE_OFFSET :]
170+
)
153171
if (
154172
old_resource.is_file
155-
and not old_resource.sha1
156-
in old_files_sha1_considered_in_deltas.keys()
173+
and not old_resource.path in old_resource_considered.keys()
157174
):
158-
path_old = "/".join(
159-
paths.split(old_resource.path)[Delta.OLD_CODEBASE_OFFSET :]
160-
)
175+
161176
if path_new == path_old:
162177
ADDED = False
163178
if new_resource.sha1 == old_resource.sha1:
164-
old_files_sha1_considered_in_deltas[
165-
old_resource.sha1
166-
] = 1
179+
old_resource_considered[old_resource.path] = 1
167180
self.create_deltas(
168181
new_resource,
169182
old_resource,
170183
path_new,
171184
path_old,
172185
0,
173-
self.stats.num_unmodified,
174186
"unmodified",
175187
)
188+
self.stats.num_unmodified += 1
176189
break
177190
else:
178-
old_files_sha1_considered_in_deltas[
179-
old_resource.sha1
180-
] = 1
191+
old_resource_considered[old_resource.path] = 1
181192
self.create_deltas(
182193
new_resource,
183194
old_resource,
184195
path_new,
185196
path_old,
186197
20,
187-
self.stats.num_modified,
188198
"modified",
189199
)
200+
self.stats.num_modified += 1
190201
break
191202
else:
192203
if new_resource.sha1 == old_resource.sha1:
193-
old_files_sha1_considered_in_deltas[
194-
old_resource.sha1
195-
] = 1
204+
old_resource_considered[old_resource.path] = 1
196205
ADDED = False
197206
self.create_deltas(
198207
new_resource,
199208
old_resource,
200209
path_new,
201210
path_old,
202211
0,
203-
self.stats.num_moved,
204212
"moved",
205213
)
214+
self.stats.num_moved += 1
206215
break
207216

208217
if ADDED:
209218
self.create_deltas(
210-
new_resource,
211-
None,
212-
path_new,
213-
None,
214-
100,
215-
self.stats.num_added,
216-
"added",
219+
new_resource, None, path_new, None, 100, "added",
217220
)
221+
self.stats.num_added += 1
218222

219223
for old_resource_remaining in self.codebase2.walk():
220224
if (
221225
old_resource_remaining.is_file
222-
and old_resource_remaining.sha1
223-
not in old_files_sha1_considered_in_deltas.keys()
226+
and old_resource_remaining.path not in old_resource_considered.keys()
224227
):
225228
path_old = "/".join(
226229
paths.split(old_resource_remaining.path)[
227230
Delta.OLD_CODEBASE_OFFSET :
228231
]
229232
)
230233
self.create_deltas(
231-
None,
232-
old_resource_remaining,
233-
None,
234-
path_old,
235-
0,
236-
self.stats.num_removed,
237-
"removed",
234+
None, old_resource_remaining, None, path_old, 0, "removed",
238235
)
236+
self.stats.num_removed += 1
239237

240238
def license_diff(self):
241239
"""
@@ -376,19 +374,23 @@ def licenses_to_dict(self, file):
376374
return []
377375

378376
def file_to_dict(self, deltacode, file, new_file=True):
377+
379378
path_offset = (
380379
Delta.NEW_CODEBASE_OFFSET if new_file else Delta.OLD_CODEBASE_OFFSET
381380
)
382381
if file:
383382
return OrderedDict(
384383
[
385-
("path", file.path),
384+
("path", "/".join(paths.split(file.path)[path_offset:])),
386385
("type", file.type),
387386
("name", file.name),
388387
("size", file.size),
389388
("sha1", file.sha1),
390-
("fingerprint", deltacode.old_files_fingerprint.get(file.path, "")),
391-
("original_path", "/".join(paths.split(file.path)[path_offset:])),
389+
(
390+
"fingerprint",
391+
file.fingerprint if hasattr(file, "fingerprint") else "",
392+
),
393+
("original_path", file.path),
392394
("licenses", self.licenses_to_dict(file)),
393395
("copyrights", self.copyrights_to_dict(file)),
394396
]
@@ -399,6 +401,11 @@ def to_dict(self, deltacode):
399401
Return an OrderedDict comprising the 'factors', 'score' and new and old
400402
'path' attributes of the object.
401403
"""
404+
if (
405+
not deltacode.options.get("--all-delta-types", "") == True
406+
and self.status == "unmodified"
407+
):
408+
return
402409
if self.new_file:
403410
new_file = self.new_file.to_dict()
404411
else:

src/deltacode/exceptions.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#
2+
# Copyright (c) 2017-2018 nexB Inc. and others. All rights reserved.
3+
# http://nexb.com and https://github.com/nexB/deltacode/
4+
# The DeltaCode software is licensed under the Apache License version 2.0.
5+
# Data generated with DeltaCode require an acknowledgment.
6+
# DeltaCode is a trademark of nexB Inc.
7+
#
8+
# You may not use this software except in compliance with the License.
9+
# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
10+
# Unless required by applicable law or agreed to in writing, software distributed
11+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
13+
# specific language governing permissions and limitations under the License.
14+
#
15+
# When you publish or redistribute any data created with DeltaCode or any DeltaCode
16+
# derivative work, you must accompany this data with the following acknowledgment:
17+
#
18+
# Generated with DeltaCode and provided on an "AS IS" BASIS, WITHOUT WARRANTIES
19+
# OR CONDITIONS OF ANY KIND, either express or implied. No content created from
20+
# DeltaCode should be considered or used as legal advice. Consult an Attorney
21+
# for any legal advice.
22+
# DeltaCode is a free and open source software analysis tool from nexB Inc. and others.
23+
# Visit https://github.com/nexB/deltacode/ for support and download.
24+
#
25+
26+
27+
class FileError(Exception):
28+
def __init__(self, *args):
29+
if args:
30+
self.message = args[0]
31+
else:
32+
self.message = None
33+
34+
def __str__(self):
35+
return self.message

src/deltacode/test_utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import json
3535

3636
from commoncode.system import on_windows
37+
from commoncode import paths
3738
from commoncode.resource import VirtualCodebase
3839

3940
def run_scan_click(options, monkeypatch=None, test_mode=True, expected_rc=0, env=None):
@@ -149,6 +150,10 @@ def streamline_errors(errors):
149150
errors[i] = cleaned_error
150151

151152

153+
def get_aligned_path(delta, path, new_file):
154+
OFFSET = delta.NEW_CODEBASE_OFFSET if new_file else delta.OLD_CODEBASE_OFFSET
155+
return "/".join(paths.split(path)[OFFSET:])
156+
152157
def streamline_headers(headers):
153158
"""
154159
Modify the `headers` list of mappings in place to make it easier to test.

src/deltacode/utils.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ def update_added_from_license_info(delta, unique_categories):
5555
one or more categories to its 'factors' attribute if there has
5656
been a license change.
5757
"""
58-
new_licenses = delta.new_file.licenses or []
58+
new_licenses = (
59+
delta.new_file.licenses if hasattr(delta.new_file, "licenses") else []
60+
)
5961

6062
new_categories = set(license["category"] for license in new_licenses)
61-
if delta.new_file.licenses:
63+
if hasattr(delta.new_file, "licenses"):
6264
delta.update(20, "license info added")
6365
for category in new_categories:
6466
# no license ==> 'Copyleft Limited'or higher
@@ -200,7 +202,7 @@ def deltas(deltacode, all_delta_types=False):
200202
for delta in deltacode.deltas:
201203
if all_delta_types is True:
202204
yield delta.to_dict(deltacode)
203-
elif not delta.is_unmodified():
205+
elif not delta.status == "unmodified":
204206
yield delta.to_dict(deltacode)
205207

206208

0 commit comments

Comments
 (0)