Add licensepy (license header formatting) to checks-superstaq#983
Conversation
richrines1
left a comment
There was a problem hiding this comment.
thank you for this!! added some inline suggestions. a few comments more generally:
- there should be no references to "server" here. Any dynamic/repo-specific behavior should be handled via arguments or the config file - we would like checks in checks-superstaq to generalize outside of our repos as much as possible (within reason)
- once we correct outdated headers once, it seems like we shouldn't need to keep checking them? in which case maybe the "outdated" functionality doesn't need to live in in this script - we can save the code you use to make these initial corrections somewhere internally, and then use this script to check headers from here on out
- if we still need to explicitly check for the string "Infleqtion" in places, maybe we could add a "licensee" value to the config file in addition to the header, instead of hard coding it?
- similarly, i can maybe see why it's unavoidable but i feel like the hard-coded "apache" checks somewhat defeat the purposes of saving the header in the config. do you think there's an easy way to check if the headers are ~the same, up to licensee/year? maybe we could allow the header in pyproject.toml to include
{YEAR}and{LICENSEE}tags, which we could convert to wildcards when comparing against existing licenses
also fwiw it's also ok if this script doesn't handle every possible case perfectly - if it gets confused it can always just throw an error saying to fix the headers manually :)
| """ | ||
| ) | ||
| parser.add_argument( | ||
| "--apply", action="store_true", help="Add the license header to files.", default=False |
There was a problem hiding this comment.
no need to set default=False with action="store_true"
| "--apply", action="store_true", help="Add the license header to files.", default=False | |
| "--apply", action="store_true", help="Add the license header to files." |
(ditto below)
| license_header = "" | ||
| exceptions = ["# pylint:", "#!/", "# mypy:"] | ||
|
|
||
| with open(file, "r+") as f: |
There was a problem hiding this comment.
do we need the "+" here? if not:
| with open(file, "r+") as f: | |
| with open(file, "r") as f: |
| try: | ||
| data: dict[str, Any] = tomlkit.parse(Path("pyproject.toml").read_text()) | ||
| expected_license_header = str(data["tool"]["license_header_format"]["license_header"]) | ||
| in_server = "Apache" not in expected_license_header |
There was a problem hiding this comment.
we should put this in a function instead executing it globally
| raise KeyError( | ||
| "Under [tool.license_header_format] add a license_header field with the license\ | ||
| heder that should be added to source code files in the repository." | ||
| ) |
There was a problem hiding this comment.
no need to raise an error in this case, we can just have an info message saying that no license header was found and then return as if it succeeded
| return f""" | ||
| Beginning at line: {self.start_line_num} | ||
| Ending at line : {self.end_line_num}\n | ||
| {self.license_header}\n""" |
There was a problem hiding this comment.
nit
| return f""" | |
| Beginning at line: {self.start_line_num} | |
| Ending at line : {self.end_line_num}\n | |
| {self.license_header}\n""" | |
| return ( | |
| f"Beginning at line: {self.start_line_num}\n" | |
| f"Ending at line : {self.end_line_num}\n\n" | |
| f"{self.license_header}\n" | |
| ) |
That makes sense. We can also keep it but change the logic a bit. After the initial fix, instead of checking for ColdQuanta in the license header, we can check if it belongs to the licensee but is a different license. This can catch cases of changing the license provider.
I added a few more fields to replace the hard-coded variables. The cirq license header check pylint plugin does something similar. However, apache 2.0 licenses seem to have 2 different formattings from what I have seen in the license headers and that would mess with the matching if we use the wild card approach. |
| and license_header.start_line_num <= line_num + 1 < license_header.end_line_num | ||
| ): | ||
| if line[-2] == ",": | ||
| prepend += line[:-1] + f" 2024 {licensee}.\n" |
There was a problem hiding this comment.
silly legal q: will we need to update the year in every file on 1/1/2025? or do they stay the same until if/when we update the file?
if the former we might want to replace 2024 with e.g. datetime.datetime.now().year. if the latter maybe we want to make the last two digits wildcards?
There was a problem hiding this comment.
will we need to update the year in every file on 1/1/2025?
No. Given that, we can pick whichever path is easier.
… are from the same license, wildcards in pyproject toml input
|
@natibek do you want someone to take this over? |
|
@dowusu-antwi, I completely forgot about finalizing to merge this before leaving. Yes, please. It shouldn't require much work. Only need to decide on whether the license check runs as part of |
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
licensepy (license header formatting) to checks-superstaq
|
Pushed some updates to make it ready to merge. In particular:
Something else I noticed: with our transition to Not sure if this is downstream from Additionally, it seems like client-superstaq/checks-superstaq/checks_superstaq/checks-pyproject.toml Lines 175 to 181 in c60c661 is not replicated/deleted upon running ./checks/licenses.py --fix.
|
richrines1
left a comment
There was a problem hiding this comment.
a few nits/questions but otherwise looks great
Co-authored-by: richrines1 <85512171+richrines1@users.noreply.github.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com> add `checks/licenses.py` Signed-off-by: Bharath <bharath.thotakura@infleqtion.com> remaining renaming Signed-off-by: Bharath <bharath.thotakura@infleqtion.com> revert bad rename Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
Signed-off-by: Bharath <bharath.thotakura@infleqtion.com>
7b81114 to
9559b2e
Compare
|
Should this be a required check in the CI @richrines1 @vtomole? |
i think so unless we have any reason not to |
@bharat-thotakura, Thank you for finishing the PR! I double-checked the implementation of the formatting, and it does add a new line after the updated license header in an attempt to consistently create separation between the new header and other content in the file. This was especially important to handle the cases where the wrong header is extracted from part of a larger comment. I'll test the outputs with Ruff and make changes so that it doesn't cause formatting issues. |
Update: #983 (comment)
This PR adds
licensepytochecks-superstaq, enabling license header formatting as configured in the project'spyproject.toml. This can be toggled inchecks/all_.pyor can be run directly viachecks/license_header_.py. This PR also adds this check to the CI for enforcement moving forwardOutdated:
There are inconsistencies in when license headers are added to source code (and whether the added ones are the same). This PR solves this with a checker that checks if the source code has a license header and if it does, whether it is the correct one. It accounts for
shebanglines, comments at the beginning of files unrelated to license headers, andpylintandmypydisable lines. This check has been added toall_.py._./checks/license_header_format_.pyfrom the root directory.-ican be used to perform an incremental check. This is not enabled by default.*.py.--no-headerflag is used to handle only cases where no header is found. By default, it is False.--bad-headerflag is used to handle only cases where an incorrect header is found. By default, it is False.--applyflag is used to fix the header problems. By default, it only checks if the headers are correct and does not make any fixes.