Enforce quality checks and type hints, improve quality and typing#73
Enforce quality checks and type hints, improve quality and typing#73bradenmacdonald merged 22 commits intomainfrom
Conversation
|
Thanks for the pull request, @bradenmacdonald! Please note that it may take us up to several weeks or months to complete a review and merge your PR. Feel free to add as much of the following information to the ticket as you can:
All technical communication about the code itself will be done via the GitHub pull request interface. As a reminder, our process documentation is here. Please let us know once your PR is ready for our review and all tests are green. |
| message=message, | ||
| published_at=published_at, | ||
| published_by=published_by, | ||
| published_by_id=published_by, |
There was a problem hiding this comment.
This was a bug: the function accepted an int but assigned it to a User model attribute.
| Check whether a LearningPackage with a particular key exists. | ||
| """ | ||
| LearningPackage.objects.filter(key=key).exists() | ||
| return LearningPackage.objects.filter(key=key).exists() |
There was a problem hiding this comment.
This was a bug - missing return statement.
45c4435 to
e73081e
Compare
1aabddf to
6886014
Compare
| ) | ||
|
|
||
|
|
||
| def is_displayable_text(mime_type): |
There was a problem hiding this comment.
This function was no longer used, after a prior refactor.
| """ | ||
| Create a new TextContent instance for the given RawContent. | ||
| """ | ||
| text = codecs.decode(raw_content.file.open().read(), encoding) |
There was a problem hiding this comment.
This was a bug (I assume): the API accepted an encoding parameter but ignored it, and always used "utf-8-sig"
| ) | ||
| if rc_created or not hasattr(raw_content, "text_content"): | ||
| text = codecs.decode(data_bytes, "utf-8-sig") | ||
| text = codecs.decode(data_bytes, encoding) |
There was a problem hiding this comment.
Same here: encoding parameter was silently ignored.
|
|
||
|
|
||
| def create_hash_digest(data_bytes): | ||
| def create_hash_digest(data_bytes: bytes) -> str: |
There was a problem hiding this comment.
With a type annotation, this argument could be renamed from data_bytes to data: bytes which is much more clear (data_bytes could be a size in bytes, for example). But the name data_bytes is already used by create_raw_content in the public API so I'm not sure if it's OK to change.
There was a problem hiding this comment.
The only place it could be used is edx-platform, so I think it's okay to change if it's not already referenced there. I also don't think it's that important to do so though.
There was a problem hiding this comment.
Yeah honestly this is a very minor thing :p
| taxonomy = Taxonomy( | ||
| name=name, | ||
| description=description, | ||
| description=description or "", |
There was a problem hiding this comment.
Per Django convention, NULL/None should not be used for text fields.
| def __init__(self, tag: dict, format: str, message: str, **kargs): | ||
| self.tag = tag | ||
| def __init__(self, tag: dict | None, format: str, message: str, **kargs): | ||
| super().__init__(tag) |
There was a problem hiding this comment.
These exception classes are quite messy and could use a refactor. Once you add type hints, the problems become more obvious. In particular, none of them were calling super().__init__(), and the purpose of the tag field is unclear. Some have hard-coded messages and some let you override the message. For now I just made the minimal changes to get type checks passing.
| tags_data = json.load(file) | ||
| except json.JSONDecodeError as error: | ||
| return None, [ | ||
| return [], [ |
There was a problem hiding this comment.
This was returning (None, list) in a clear violation of the declared return type (list, list)
| errors = cls._verify_header(list(header_fields or [])) | ||
| if errors: | ||
| return None, errors | ||
| return [], errors |
There was a problem hiding this comment.
Same here, invalid return of None
| return task.status | ||
| if task is None: | ||
| raise ValueError("No import task was created yet.") | ||
| return TagImportTaskState(task.status) |
There was a problem hiding this comment.
This was a bug: the typing declared that it returned enum instances, but it was returning the enum values which are just plain strings. Also it wasn't handling the case where there was no "last import task".
| ), | ||
| ) | ||
| description = MultiCollationTextField( | ||
| null=True, |
There was a problem hiding this comment.
Django convention is not not use NULL for text fields, unless there's a very good reason, which doesn't seem to be the case here.
6e2dfdc to
e9fabfc
Compare
647babd to
0697182
Compare
0697182 to
ee19093
Compare
b17a5fe to
488dac4
Compare
| ) == expected | ||
| # FIXME: something is wrong with this test case. It's setting the string | ||
| # "tag_id" as the primary key (integer) of the Tag instance, and it mentions | ||
| # "parent validation" but there is nothing to do with parents here. |
There was a problem hiding this comment.
I'm not sure what was going on with this test case. It was failing and I don't know what it's even trying to do, so for now I commented it out.
There was a problem hiding this comment.
@ChrisChV Could you please check this when you have time?
| object_id="id", | ||
| tag=tag, | ||
| ) | ||
| self.language_taxonomy.validate_object_tag( |
There was a problem hiding this comment.
This function just returns a bool so this test was having no effect. I commented it out because again I'm not sure what it's supposed to be doing.
There was a problem hiding this comment.
Hi @bradenmacdonald! Awesome work here.
I just pointed out some nits, but feel free to push back if you want to!
We are working a lot with this repository, so we should merge this ASAP to avoid a rebase hell.
Question:
- Is there a chance that we break
edx-platform(if the version is not pinned) because we forced the types here, and there is a wrong call onedx-platformside (or another package that imports this package)?
PS: Running make quality I got a warning on the console:
pylint: Command line or configuration file:1: UserWarning: Specifying exception names in the overgeneral-exceptions option without module name is deprecated and support for it will be removed in pylint 3.0. Use fully qualified name (maybe 'builtins.Exception' ?) instead.
Changing the pylintrc as suggested in the warning removed it.
[EXCEPTIONS]
overgeneral-exceptions = builtins.Exception
|
@rpenido Thanks for the great review! (and fast!) I agree that we should merge this ASAP because of the large number of possible conflicts.
The
Nope. Python ignores the types at runtime, and on the edx-platform side they're not even being checked yet. Actually once this PR is merged, I'm going to modify edx-platform so that they are checked.
Sorry, I didn't mention that. You need to edit |
|
@ormsbee Since Jill is on leave, would you be able to approve merging this PR? |
ormsbee
left a comment
There was a problem hiding this comment.
Looks good to me. I realize there are some follow-up items from this (like the test), but it's probably better to merge this ASAP given it's size and breadth.
Thanks @bradenmacdonald. This is one of those things that I really wanted to see happen but didn't get around to. It warms my heart to see this sort of thing revealing bugs.
|
@bradenmacdonald 🎉 Your pull request was merged! Please take a moment to answer a two question survey so we can improve your experience in the future. |
As it turns out, the CI checks for this repo were not running pylint or mypy, and there were a ton of lint errors and typing bugs.
This repo's packages also didn't have any
py.typedfiles, so whatever type hints they declared were being ignored by other code like edx-platform code that uses this.This PR:
mypyto do static type checking of the code.mypyand to improve its qualityA few of these issues automatically identified are clearly bugs, which are now fixed :)
Private-ref: FAL-3476