-
-
Notifications
You must be signed in to change notification settings - Fork 27
Add Copyright class and copyright_diff() #82 #83 #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
00b6e7a
f8f5861
f1c9f4d
59b502d
d2cb79d
195eb00
4199bcd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,6 +57,7 @@ def __init__(self, new_path, old_path, options): | |
| self.determine_delta() | ||
| self.determine_moved() | ||
| self.license_diff() | ||
| self.copyright_diff() | ||
| # Sort deltas by score, descending, i.e., high > low. | ||
| self.deltas.sort(key=lambda Delta: Delta.score, reverse=True) | ||
|
|
||
|
|
@@ -191,28 +192,68 @@ def license_diff(self): | |
| attribute -- if there has been a license change and depending on the | ||
| nature of that change. | ||
| """ | ||
| for i in self.deltas: | ||
| if 20 <= i.score < 100: | ||
| for delta in self.deltas: | ||
| if 20 <= delta.score < 100: | ||
|
|
||
| new_licenses = i.new_file.licenses or [] | ||
| old_licenses = i.old_file.licenses or [] | ||
| new_licenses = delta.new_file.licenses or [] | ||
| old_licenses = delta.old_file.licenses or [] | ||
|
|
||
| if len(i.new_file.licenses) > 0 and i.old_file.licenses == []: | ||
| i.factors.append('license info added') | ||
| i.score += 20 | ||
| if len(delta.new_file.licenses) > 0 and delta.old_file.licenses == []: | ||
| delta.factors.append('license info added') | ||
| delta.score += 20 | ||
| return | ||
|
|
||
| if i.new_file.licenses == [] and len(i.old_file.licenses) > 0: | ||
| i.factors.append('license info removed') | ||
| i.score += 15 | ||
| if delta.new_file.licenses == [] and len(delta.old_file.licenses) > 0: | ||
| delta.factors.append('license info removed') | ||
| delta.score += 15 | ||
| return | ||
|
|
||
| new_keys = set(l.key for l in new_licenses) | ||
| old_keys = set(l.key for l in old_licenses) | ||
| new_keys = set(license.key for license in new_licenses) | ||
| old_keys = set(license.key for license in old_licenses) | ||
|
|
||
| if new_keys != old_keys: | ||
| i.factors.append('license change') | ||
| i.score += 10 | ||
| delta.factors.append('license change') | ||
| delta.score += 10 | ||
|
|
||
| def copyright_diff(self): | ||
| """ | ||
| Compare the copyright details for a pair of 'new' and 'old' File objects | ||
| in a Delta object and change the Delta object's 'score' attribute -- | ||
| and add an appropriate category (e.g., 'copyright info removed', 'copyright | ||
| info added' or 'copyright change') to the Delta object's 'factors' | ||
| attribute -- if there has been a copyright change and depending on the | ||
| nature of that change. | ||
| """ | ||
| for delta in self.deltas: | ||
| if 20 <= delta.score < 100: | ||
|
|
||
| new_copyrights = delta.new_file.copyrights or [] | ||
| old_copyrights = delta.old_file.copyrights or [] | ||
|
|
||
| if len(delta.new_file.copyrights) > 0 and delta.old_file.copyrights == []: | ||
| delta.factors.append('copyright info added') | ||
| delta.score += 15 | ||
| return | ||
|
|
||
| if delta.new_file.copyrights == [] and len(delta.old_file.copyrights) > 0: | ||
| delta.factors.append('copyright info removed') | ||
| delta.score += 10 | ||
| return | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably best that we score these the same. This will also allow for you to combine the logic into a single if statement |
||
|
|
||
| new_statements = set(statement for copyright in new_copyrights for statement in copyright.statements) | ||
| old_statements = set(statement for copyright in old_copyrights for statement in copyright.statements) | ||
|
|
||
| new_holders = set(holder for copyright in new_copyrights for holder in copyright.holders) | ||
| old_holders = set(holder for copyright in old_copyrights for holder in copyright.holders) | ||
|
|
||
| new_authors = set(author for copyright in new_copyrights for author in copyright.authors) | ||
| old_authors = set(author for copyright in old_copyrights for author in copyright.authors) | ||
|
|
||
| if ((new_statements != old_statements) or | ||
| (new_holders != old_holders) or | ||
| (new_authors != old_authors)): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should ignore looking at copyright authors for now unless we have a good reason to use them. Also I believe we need to rely more on holders. This needs some thought though
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @MaJuRG I've made this change -- let me know when you'd like to discuss how we could place greater reliance on |
||
| delta.factors.append('copyright change') | ||
| delta.score += 5 | ||
|
|
||
| def index_deltas(self, index_key='path', delta_list=[]): | ||
| """ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -161,13 +161,20 @@ def __init__(self, dictionary={}): | |
| self.sha1 = dictionary.get('sha1', '') | ||
| self.original_path = '' | ||
| self.licenses = self.get_licenses(dictionary) if dictionary.get('licenses') else [] | ||
| self.copyrights = self.get_copyrights(dictionary) if dictionary.get('copyrights') else [] | ||
|
|
||
| def get_licenses(self, dictionary): | ||
| if dictionary.get('licenses') == []: | ||
| return [] | ||
| else: | ||
| return [License(l) for l in dictionary.get('licenses')] | ||
|
|
||
| def get_copyrights(self, dictionary): | ||
| if dictionary.get('copyrights') == []: | ||
| return [] | ||
| else: | ||
| return [Copyright(l) for l in dictionary.get('copyrights')] | ||
|
|
||
| def to_dict(self): | ||
| d = OrderedDict([ | ||
| ('path', self.path), | ||
|
|
@@ -180,6 +187,13 @@ def to_dict(self): | |
|
|
||
| if self.licenses: | ||
| d['licenses'] = [l.to_dict() for l in self.licenses] | ||
| else: | ||
| d['licenses'] = [] | ||
|
|
||
| if self.copyrights: | ||
| d['copyrights'] = [l.to_dict() for l in self.copyrights] | ||
| else: | ||
| d['copyrights'] = [] | ||
|
|
||
| return d | ||
|
|
||
|
|
@@ -199,7 +213,8 @@ def __repr__(self): | |
|
|
||
| class License(object): | ||
| """ | ||
| License object created from the 'license' field in an ABCD formatted 'file' dictionary. | ||
| License object created from the 'license' field in an ABCD formatted 'file' | ||
| dictionary. | ||
| """ | ||
| def __init__(self, dictionary={}): | ||
| self.key = dictionary.get('key') | ||
|
|
@@ -225,7 +240,39 @@ def to_dict(self): | |
|
|
||
| def __repr__(self): | ||
| """ | ||
| Return string containing a printable representation of the License object. | ||
| Return string containing a printable representation of the License | ||
| object. | ||
| """ | ||
| return "%s" % self.__dict__ | ||
|
|
||
|
|
||
| class Copyright(object): | ||
| """ | ||
| Copyright object created from the 'copyrights' field in an ABCD formatted | ||
| 'file' dictionary. | ||
| """ | ||
| def __init__(self, dictionary={}): | ||
| self.statements = dictionary.get('statements') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you haven’t already, you should add tests cases where a files have large numbers of copyrights holders and statements to see if we choke somewhere, espically in the output
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also tests where there are strange characters or accent marks in the copyright statements/holders. All of these additional test cases I mentioned should probably come from scancode generated output directly as opposed to crafting the test object by hand.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @MaJuRG Since we want to use ScanCode-generated output, do you have any codebases in mind that satisfy the characteristics you describe? I've started to work my way through the codebases we've worked with (openssl, zlib et al.) but I've not yet seen large numbers of copyright holders/statements or unusual characters.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @johnmhoran You will probably just have to hand-create a file or files that contain a bunch of copyright statements
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK. Thanks, @MaJuRG .
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @MaJuRG I'm finding that an error is thrown when I include a French accent character in the copyright
While PEP 263 gives some suggestions, it's not clear to me how we can apply these to handle our input. I've done some searching in the ScanCode repo -- surely ScanCode must be able to handle such characters -- but have not yet found how ScanCode addresses this.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this issue might be addressed in scancode-toolkit/src/commoncode/text.py?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @johnmhoran where is this "statements" located?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @MaJuRG Would it be easier for you if I commit and push? Except for this one failing test, it's ready for your review. |
||
| self.holders = dictionary.get('holders') | ||
| self.authors = dictionary.get('authors') | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like I mentioned above, lets remove authors. |
||
| def to_dict(self): | ||
| """ | ||
| Given a Copyright object, return an OrderedDict with the full | ||
| set of fields from the ScanCode 'copyrights' value. | ||
| """ | ||
| d = OrderedDict([ | ||
| ('statements', self.statements), | ||
| ('holders', self.holders), | ||
| ('authors', self.authors) | ||
| ]) | ||
|
|
||
| return d | ||
|
|
||
| def __repr__(self): | ||
| """ | ||
| Return string containing a printable representation of the Copyright | ||
| object. | ||
| """ | ||
| return "%s" % self.__dict__ | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Factors,Score,Path,Name,Type,Size,Old Path | ||
| modified license info added copyright info added,55,path.txt,path.txt,file,300, |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| { | ||
| "scancode_notice": "Generated with ScanCode and provided on an \"AS IS\" BASIS, WITHOUT WARRANTIES\nOR CONDITIONS OF ANY KIND, either express or implied. No content created from\nScanCode should be considered or used as legal advice. Consult an Attorney\nfor any legal advice.\nScanCode is a free software code scanning tool from nexB Inc. and others.\nVisit https://github.com/nexB/scancode-toolkit/ for support and download.", | ||
| "scancode_version": "2.1.0", | ||
| "scancode_options": { | ||
| "--license": true, | ||
| "--info": true | ||
| }, | ||
| "files_count": 2, | ||
| "files": [ | ||
| { | ||
| "path": "new/default.txt", | ||
| "type": "file", | ||
| "name": "default.txt", | ||
| "size": 100, | ||
| "sha1": "a", | ||
| "licenses": [], | ||
| "copyrights": [] | ||
| }, | ||
| { | ||
| "path": "new/path.txt", | ||
| "type": "file", | ||
| "name": "path.txt", | ||
| "size": 300, | ||
| "sha1": "b_modified", | ||
| "licenses": [ | ||
| { | ||
| "key": "gpl-1.0-plus", | ||
| "score": 20.0, | ||
| "short_name": "GPL 1.0 or later", | ||
| "category": "Copyleft", | ||
| "owner": "Free Software Foundation (FSF)", | ||
| "homepage_url": "http://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html", | ||
| "text_url": "http://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html", | ||
| "reference_url": "https://enterprise.dejacode.com/urn/urn:dje:license:gpl-1.0-plus", | ||
| "spdx_license_key": "GPL-1.0+", | ||
| "spdx_url": "https://spdx.org/licenses/GPL-1.0", | ||
| "start_line": 353, | ||
| "end_line": 353, | ||
| "matched_rule": { | ||
| "identifier": "gpl_63.RULE", | ||
| "license_choice": false, | ||
| "licenses": [ | ||
| "gpl-1.0-plus" | ||
| ] | ||
| } | ||
| } | ||
| ], | ||
| "copyrights": [ | ||
| { | ||
| "statements": [ | ||
| "Copyright (c) 2016 Mark Adler" | ||
| ], | ||
| "holders": [ | ||
| "Mark Adler" | ||
| ], | ||
| "authors": [], | ||
| "start_line": 1, | ||
| "end_line": 3 | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| { | ||
| "scancode_notice": "Generated with ScanCode and provided on an \"AS IS\" BASIS, WITHOUT WARRANTIES\nOR CONDITIONS OF ANY KIND, either express or implied. No content created from\nScanCode should be considered or used as legal advice. Consult an Attorney\nfor any legal advice.\nScanCode is a free software code scanning tool from nexB Inc. and others.\nVisit https://github.com/nexB/scancode-toolkit/ for support and download.", | ||
| "scancode_version": "2.1.0", | ||
| "scancode_options": { | ||
| "--license": true, | ||
| "--info": true | ||
| }, | ||
| "files_count": 2, | ||
| "files": [ | ||
| { | ||
| "path": "old/default.txt", | ||
| "type": "file", | ||
| "name": "default.txt", | ||
| "size": 100, | ||
| "sha1": "a", | ||
| "licenses": [], | ||
| "copyrights": [] | ||
| }, | ||
| { | ||
| "path": "old/path.txt", | ||
| "type": "file", | ||
| "name": "path.txt", | ||
| "size": 300, | ||
| "sha1": "b", | ||
| "licenses": [], | ||
| "copyrights": [] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Factors,Score,Path,Name,Type,Size,Old Path | ||
| modified license info removed copyright info removed,45,path.txt,path.txt,file,300, |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| { | ||
| "scancode_notice": "Generated with ScanCode and provided on an \"AS IS\" BASIS, WITHOUT WARRANTIES\nOR CONDITIONS OF ANY KIND, either express or implied. No content created from\nScanCode should be considered or used as legal advice. Consult an Attorney\nfor any legal advice.\nScanCode is a free software code scanning tool from nexB Inc. and others.\nVisit https://github.com/nexB/scancode-toolkit/ for support and download.", | ||
| "scancode_version": "2.1.0", | ||
| "scancode_options": { | ||
| "--license": true, | ||
| "--info": true | ||
| }, | ||
| "files_count": 2, | ||
| "files": [ | ||
| { | ||
| "path": "new/default.txt", | ||
| "type": "file", | ||
| "name": "default.txt", | ||
| "size": 100, | ||
| "sha1": "a", | ||
| "licenses": [], | ||
| "copyrights": [] | ||
| }, | ||
| { | ||
| "path": "new/path.txt", | ||
| "type": "file", | ||
| "name": "path.txt", | ||
| "size": 300, | ||
| "sha1": "b_modified", | ||
| "licenses": [], | ||
| "copyrights": [] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| { | ||
| "scancode_notice": "Generated with ScanCode and provided on an \"AS IS\" BASIS, WITHOUT WARRANTIES\nOR CONDITIONS OF ANY KIND, either express or implied. No content created from\nScanCode should be considered or used as legal advice. Consult an Attorney\nfor any legal advice.\nScanCode is a free software code scanning tool from nexB Inc. and others.\nVisit https://github.com/nexB/scancode-toolkit/ for support and download.", | ||
| "scancode_version": "2.1.0", | ||
| "scancode_options": { | ||
| "--license": true, | ||
| "--info": true | ||
| }, | ||
| "files_count": 2, | ||
| "files": [ | ||
| { | ||
| "path": "old/default.txt", | ||
| "type": "file", | ||
| "name": "default.txt", | ||
| "size": 100, | ||
| "sha1": "a", | ||
| "licenses": [], | ||
| "copyrights": [] | ||
| }, | ||
| { | ||
| "path": "old/path.txt", | ||
| "type": "file", | ||
| "name": "path.txt", | ||
| "size": 300, | ||
| "sha1": "b", | ||
| "licenses": [ | ||
| { | ||
| "key": "gpl-1.0-plus", | ||
| "score": 20.0, | ||
| "short_name": "GPL 1.0 or later", | ||
| "category": "Copyleft", | ||
| "owner": "Free Software Foundation (FSF)", | ||
| "homepage_url": "http://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html", | ||
| "text_url": "http://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html", | ||
| "reference_url": "https://enterprise.dejacode.com/urn/urn:dje:license:gpl-1.0-plus", | ||
| "spdx_license_key": "GPL-1.0+", | ||
| "spdx_url": "https://spdx.org/licenses/GPL-1.0", | ||
| "start_line": 353, | ||
| "end_line": 353, | ||
| "matched_rule": { | ||
| "identifier": "gpl_63.RULE", | ||
| "license_choice": false, | ||
| "licenses": [ | ||
| "gpl-1.0-plus" | ||
| ] | ||
| } | ||
| } | ||
| ], | ||
| "copyrights": [ | ||
| { | ||
| "statements": [ | ||
| "Copyright (c) 2016 Mark Adler" | ||
| ], | ||
| "holders": [ | ||
| "Mark Adler" | ||
| ], | ||
| "authors": [], | ||
| "start_line": 1, | ||
| "end_line": 3 | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Factors,Score,Path,Name,Type,Size,Old Path | ||
| modified copyright change,25,path.txt,path.txt,file,300, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should make a method that down
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turn this logic into a function where you pass a score and a string. Then we can replace lines like 207 and 208 with a single function call instead of two line tweaks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add tests for this as well