Skip to content

Commit a095146

Browse files
authored
Merge pull request #167 from Pratikrocks/VC_determine_delta
Virtual Codebase modification of the determine deltas
2 parents 6b50a68 + 92c3355 commit a095146

103 files changed

Lines changed: 3379 additions & 2209 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
34+
commoncode>=21.6.11
3535
click
3636
simplejson
3737
unicodecsv

src/deltacode/__init__.py

Lines changed: 322 additions & 199 deletions
Large diffs are not rendered by default.

src/deltacode/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ def write_json(deltacode, outfile, all_delta_types=False):
4444
"""
4545
results = OrderedDict([
4646
('deltacode_notice', get_notice()),
47+
# ('new_scan_options', deltacode.new_scan_options),
48+
# ('old_scan_options', deltacode.old_scan_options),
4749
('deltacode_options', deltacode.options),
4850
('deltacode_version', __version__),
4951
('deltacode_errors', collect_errors(deltacode)),
@@ -88,6 +90,5 @@ def cli(new, old, json_file, all_delta_types):
8890

8991
# do the delta
9092
deltacode = DeltaCode(new, old, options)
91-
9293
# generate JSON output
9394
write_json(deltacode, json_file, all_delta_types)

src/deltacode/test_utils.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
import json
3535

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

3940
def run_scan_click(options, monkeypatch=None, test_mode=True, expected_rc=0, env=None):
4041
"""
@@ -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: 103 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import os
3434

3535
from commoncode import paths
36+
from collections import OrderedDict
37+
3638

3739
def update_from_license_info(delta, unique_categories):
3840
"""
@@ -53,19 +55,20 @@ def update_added_from_license_info(delta, unique_categories):
5355
one or more categories to its 'factors' attribute if there has
5456
been a license change.
5557
"""
56-
new_licenses = delta.new_file.licenses or []
57-
new_categories = set(license.category for license in new_licenses)
58-
59-
if delta.new_file.has_licenses():
60-
delta.update(20, 'license info added')
58+
new_licenses = (
59+
delta.new_file.licenses if hasattr(delta.new_file, "licenses") else []
60+
)
6161

62+
new_categories = set(license["category"] for license in new_licenses)
63+
if hasattr(delta.new_file, "licenses"):
64+
delta.update(20, "license info added")
6265
for category in new_categories:
6366
# no license ==> 'Copyleft Limited'or higher
6467
if category in unique_categories:
65-
delta.update(20, category.lower() + ' added')
68+
delta.update(20, category.lower() + " added")
6669
# no license ==> 'Permissive' or 'Public Domain'
6770
else:
68-
delta.update(0, category.lower() + ' added')
71+
delta.update(0, category.lower() + " added")
6972
return
7073

7174

@@ -75,44 +78,50 @@ def update_modified_from_license_info(delta, unique_categories):
7578
one or more categories to its 'factors' attribute if there has
7679
been a license change.
7780
"""
78-
if not delta.new_file.has_licenses() and delta.old_file.has_licenses():
79-
delta.update(15, 'license info removed')
80-
return
8181

82-
new_licenses = delta.new_file.licenses or []
83-
old_licenses = delta.old_file.licenses or []
82+
new_licenses = (
83+
delta.new_file.licenses if hasattr(delta.new_file, "licenses") else []
84+
)
85+
old_licenses = (
86+
delta.old_file.licenses if hasattr(delta.old_file, "licenses") else []
87+
)
88+
89+
if not new_licenses and old_licenses:
90+
delta.update(15, "license info removed")
91+
return
8492

85-
new_categories = set(license.category for license in new_licenses)
86-
old_categories = set(license.category for license in old_licenses)
93+
new_categories = set(license.get("category", "") for license in new_licenses)
94+
old_categories = set(license.get("category", "") for license in old_licenses)
8795

88-
if delta.new_file.has_licenses() and not delta.old_file.has_licenses():
89-
delta.update(20, 'license info added')
96+
if new_licenses and not old_licenses:
97+
delta.update(20, "license info added")
9098

9199
for category in new_categories:
92100
# no license ==> 'Copyleft Limited'or higher
93101
if category in unique_categories:
94-
delta.update(20, category.lower() + ' added')
102+
delta.update(20, category.lower() + " added")
95103
# no license ==> 'Permissive' or 'Public Domain'
96104
else:
97-
delta.update(0, category.lower() + ' added')
105+
delta.update(0, category.lower() + " added")
98106
return
99107

100-
new_keys = set(license.key for license in new_licenses)
101-
old_keys = set(license.key for license in old_licenses)
108+
new_keys = set(license.get("key", "") for license in new_licenses)
109+
old_keys = set(license.get("key", "") for license in old_licenses)
102110

103111
if new_keys != old_keys:
104-
delta.update(10, 'license change')
112+
113+
delta.update(10, "license change")
105114
for category in new_categories - old_categories:
106115
unique_categories_in_old_file = len(old_categories & unique_categories)
107116
# 'Permissive' or 'Public Domain' ==> 'Copyleft Limited' or higher
108117
if unique_categories_in_old_file == 0 and category in unique_categories:
109-
delta.update(20, category.lower() + ' added')
118+
delta.update(20, category.lower() + " added")
110119
# at least 1 category in the old file was 'Copyleft Limited' or higher ==> 'Copyleft Limited' or higher
111120
elif unique_categories_in_old_file != 0 and category in unique_categories:
112-
delta.update(10, category.lower() + ' added')
121+
delta.update(10, category.lower() + " added")
113122
# 'Permissive' or 'Public Domain' ==> 'Permissive' or 'Public Domain' if not in old_categories
114123
elif category not in unique_categories:
115-
delta.update(0, category.lower() + ' added')
124+
delta.update(0, category.lower() + " added")
116125

117126

118127
def update_from_copyright_info(delta):
@@ -134,8 +143,9 @@ def update_added_from_copyright_info(delta):
134143
one or more categories to its 'factors' attribute if there has
135144
been a copyright change.
136145
"""
137-
if delta.new_file.has_copyrights():
138-
delta.update(10, 'copyright info added')
146+
147+
if hasattr(delta.new_file, "copyrights"):
148+
delta.update(10, "copyright info added")
139149
return
140150

141151

@@ -145,27 +155,39 @@ def update_modified_from_copyright_info(delta):
145155
one or more categories to its 'factors' attribute if there has
146156
been a copyright change.
147157
"""
148-
new_copyrights = delta.new_file.copyrights or []
149-
old_copyrights = delta.old_file.copyrights or []
150158

151-
if delta.new_file.has_copyrights() and not delta.old_file.has_copyrights():
152-
delta.update(10, 'copyright info added')
159+
new_copyrights = (
160+
delta.new_file.copyrights if hasattr(delta.new_file, "copyrights") else []
161+
)
162+
old_copyrights = (
163+
delta.old_file.copyrights if hasattr(delta.old_file, "copyrights") else []
164+
)
165+
166+
if new_copyrights and not old_copyrights:
167+
delta.update(10, "copyright info added")
153168
return
154-
if not delta.new_file.has_copyrights() and delta.old_file.has_copyrights():
155-
delta.update(10, 'copyright info removed')
169+
if not new_copyrights and old_copyrights:
170+
delta.update(10, "copyright info removed")
156171
return
157172

158-
new_holders = set(holder for copyright in new_copyrights for holder in copyright.holders)
159-
old_holders = set(holder for copyright in old_copyrights for holder in copyright.holders)
160-
173+
new_holders = set(
174+
holder
175+
for copyright in new_copyrights
176+
for holder in copyright.get("holders", [])
177+
)
178+
old_holders = set(
179+
holder
180+
for copyright in old_copyrights
181+
for holder in copyright.get("holders", [])
182+
)
161183
if new_holders != old_holders:
162-
delta.update(5, 'copyright change')
184+
delta.update(5, "copyright change")
163185

164186

165187
def collect_errors(deltacode):
166188
errors = []
167-
errors.extend(deltacode.new.errors)
168-
errors.extend(deltacode.old.errors)
189+
errors.extend(deltacode.new_files_errors)
190+
errors.extend(deltacode.old_files_errors)
169191
errors.extend(deltacode.errors)
170192

171193
return errors
@@ -179,39 +201,57 @@ def deltas(deltacode, all_delta_types=False):
179201
"""
180202
for delta in deltacode.deltas:
181203
if all_delta_types is True:
182-
yield delta.to_dict()
183-
elif not delta.is_unmodified():
184-
yield delta.to_dict()
204+
yield delta.to_dict(deltacode)
205+
elif not delta.status == "unmodified":
206+
yield delta.to_dict(deltacode)
207+
185208

186209
def calculate_percent(value, total):
187210
"""
188211
Return the rounded value percentage of total.
189212
"""
190-
ratio = (value / total) * 100
191-
return round(ratio, 2)
213+
try:
214+
ratio = (value / total) * 100
215+
return round(ratio, 2)
216+
except ZeroDivisionError:
217+
return 0
218+
192219

193220
class AlignmentException(Exception):
194221
"""
195222
Named exception for alignment errors.
196223
"""
224+
197225
pass
198226

199-
def align_trees(a_files, b_files):
227+
228+
class FileError(Exception):
200229
"""
201-
Given two sequences of File objects 'a' and 'b', return a tuple of
202-
two integers that represent the number path segments to remove
203-
respectively from a File path in 'a' or a File path in 'b' to obtain the
204-
equal paths for two files that are the same in 'a' and 'b'.
230+
Named Exception for handling errors which could be raised due to
231+
unsupported errors in the json file
232+
"""
233+
def __init__(self, *args):
234+
if args:
235+
self.message = args[0]
236+
else:
237+
self.message = None
238+
239+
def __str__(self):
240+
return self.message
241+
242+
243+
def align_trees(codebase1, codebase2):
244+
"""
245+
Aligns the path of the two codebases
205246
"""
206-
# we need to find one uniquly named file that exists in 'a' and 'b'.
207247
a_names = defaultdict(list)
208-
for a_file in a_files:
209-
a_names[a_file.name].append(a_file)
248+
for resource in codebase1.walk():
249+
a_names[resource.name].append(resource)
210250
a_uniques = {k: v[0] for k, v in a_names.items() if len(v) == 1}
211251

212252
b_names = defaultdict(list)
213-
for b_file in b_files:
214-
b_names[b_file.name].append(b_file)
253+
for resource in codebase2.walk():
254+
b_names[resource.name].append(resource)
215255
b_uniques = {k: v[0] for k, v in b_names.items() if len(v) == 1}
216256

217257
candidate_found = False
@@ -228,59 +268,31 @@ def align_trees(a_files, b_files):
228268
if a_unique.path == b_unique.path:
229269
return 0, 0
230270

231-
common_suffix, common_segments = paths.common_path_suffix(a_unique.path, b_unique.path)
271+
common_suffix, common_segments = paths.common_path_suffix(
272+
a_unique.path, b_unique.path
273+
)
232274
a_segments = len(paths.split(a_unique.path))
233275
b_segments = len(paths.split(b_unique.path))
234276

235277
return a_segments - common_segments, b_segments - common_segments
236278

237279

238-
def fix_trees(a_files, b_files):
239-
"""
240-
Given two sequences of File objects 'a' and 'b', use the tuple of two
241-
integers returned by align_trees() to remove the number of path segments
242-
required to create equal paths for two files that are the same in 'a' and
243-
'b'.
244-
"""
245-
a_offset, b_offset = align_trees(a_files, b_files)
246-
for a_file in a_files:
247-
a_file.original_path = a_file.path
248-
a_file.path = '/'.join(paths.split(a_file.path)[a_offset:])
249-
250-
for b_file in b_files:
251-
b_file.original_path = b_file.path
252-
b_file.path = '/'.join(paths.split(b_file.path)[b_offset:])
253-
254-
255-
def check_moved(added_sha1, added_deltas, removed_sha1, removed_deltas):
256-
"""
257-
Return True if there is only one pair of matching 'added' and 'removed'
258-
Delta objects and their respective File objects have the same 'name' attribute.
259-
"""
260-
if added_sha1 != removed_sha1:
261-
return False
262-
if len(added_deltas) != 1 or len(removed_deltas) != 1:
263-
return False
264-
if added_deltas[0].new_file.name == removed_deltas[0].old_file.name:
265-
return True
266-
267-
268280
def get_notice():
269281
"""
270282
Retrieve the notice text from the NOTICE file for display in the JSON output.
271283
"""
272-
notice_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'NOTICE')
284+
notice_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "NOTICE")
273285
notice_text = open(notice_path).read()
274286

275-
delimiter = '\n\n\n'
287+
delimiter = "\n\n\n"
276288
[notice_text, extra_notice_text] = notice_text.split(delimiter, 1)
277289
extra_notice_text = delimiter + extra_notice_text
278290

279-
delimiter = '\n\n '
291+
delimiter = "\n\n "
280292
[notice_text, acknowledgment_text] = notice_text.split(delimiter, 1)
281293
acknowledgment_text = delimiter + acknowledgment_text
282294

283-
notice = acknowledgment_text.strip().replace(' ', '')
295+
notice = acknowledgment_text.strip().replace(" ", "")
284296

285297
return notice
286298

@@ -296,6 +308,7 @@ def hamming_distance(fingerprint1, fingerprint2):
296308

297309
return result
298310

311+
299312
def bitarray_from_hex(fingerprint_hex):
300313
"""
301314
Return bitarray from a hex string.
@@ -305,11 +318,12 @@ def bitarray_from_hex(fingerprint_hex):
305318

306319
return result
307320

321+
308322
def bitarray_from_bytes(b):
309323
"""
310324
Return bitarray from a byte string, interpreted as machine values.
311325
"""
312326
a = bitarray()
313327
a.frombytes(b)
314328

315-
return a
329+
return a

tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)