-
Notifications
You must be signed in to change notification settings - Fork 4
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
Unclear code in check
around supporting -p
and -l
flags
#119
Comments
Just quickly glanced at it and indeed, this code block is very hard to understand! Best to refactor or at least comment after we uncovered its use again😄 I will find some time today to check if/what these lines do and then come back to you. Are you planning to contribute/extend or do you have different use cases in mind that are currently not supported? cheers! |
Thanks! I am currently modifying the project to pull license info from a conda environment directory rather than pip distributions. |
okay, this is something that exists locally only? dont know muxh about conda, habe not used it much and not for long time. Is thisnsomething that's required often? |
There are two main ways to install a Python package, from PyPI and from a conda repository. Every package installed by conda has a metadata file which includes all of the good stuff, including license -- yes, locally. Conda is relatively popular. My company uses it over venv since you can install non-Python libraries as well. It's a generic package manager with a strong leaning towards Python. |
I see, thanks for the explanation! Would be nice then if we could already here also incorporate the license info in the metadata file. I'd see this as a very valuable extension to the current (very pypi-centric) feature set! Would you consider contributing this? |
Possibly, however first we'd need to open-source the code that I am writing, which will take time. Also, for the internal solution, I removed all PyPI support since we do not need it. So contributing would require having a plan on how to support both. |
yes we'll need to see the requirements and how this could be integrated. re the code, sorry it was very busy the last few days, I hope to come back to you today |
No rush! |
I believe that the line of code in question https://github.com/ubersan/pylic/blob/main/pylic/cli/commands/check.py#L89-L98 should be: if not any(
[
unnecessary_ignore_packages,
bad_unsafe_packages,
missing_unsafe_packages,
unsafe_licenses.found,
]
): Notice the The described logic is very much an |
sooo, finally found an hour to dig into this. First of all, the code is hugely complicated and of course there are bugs. I found a few examples where the return code is wrong. Yes you're wrong, this code should basically just deal with the scenario where there were extra/unnecessary packages or licenses listed, which would normally lead to an error (return code The snippet can be rewritten into something like this: extra_packages_declared_and_allowed = (
len(unnecessary_unsafe_packages) > 0 or len(unnecessary_ignore_packages) > 0
) and "allow_extra_packages" in options
if extra_packages_declared_and_allowed and not any(
[
bad_unsafe_packages,
missing_unsafe_packages,
unsafe_licenses.found,
unnecessary_safe_licenses,
]
):
console_writer.write_all_licenses_ok()
return 0
extra_licenses_declared_and_allowed = len(unnecessary_safe_licenses) > 0 and "allow_extra_licenses" in options
if extra_licenses_declared_and_allowed and not any(
[
unnecessary_ignore_packages,
bad_unsafe_packages,
missing_unsafe_packages,
unsafe_licenses.found,
unnecessary_unsafe_packages,
]
):
console_writer.write_all_licenses_ok()
return 0
if (
extra_licenses_declared_and_allowed
and extra_packages_declared_and_allowed
and not any([bad_unsafe_packages, missing_unsafe_packages, unsafe_licenses.found])
):
console_writer.write_all_licenses_ok()
return 0 which already is much easier to understand and it's clearer what is happening. Basically we test if the exception case is applicable and no other un-silenced error is present. This cross-checking makes it a bit harder, as we e.g. only check extra packages and then return def test_temp(
check_command: CheckCommand,
package: str,
license: str,
version: str,
read_config: MagicMock,
read_all_installed_licenses_metadata: MagicMock,
) -> None:
unsafe_packages = [f"{package}", "some-other-package-124"]
ignore_packages = unsafe_packages
installed_licenses = [
{"package": f"{package}", "license": license, "version": version},
{"package": "some-other-package", "license": "THIS-IS-IGNORED", "version": version},
]
safe_licenses = [license]
read_config.return_value = AppConfig(safe_licenses, unsafe_packages, ignore_packages)
read_all_installed_licenses_metadata.return_value = installed_licenses
return_code = check_command.handle(["allow_extra_packages"])
assert return_code == 1 # This fails, it returns 0 Switching to We have to fix this soon and rewrite this so it's much easier to understand. Not sure if the extended version above fixes everything, at least the failing test now would pass. But we need to spend a bit more time on this and ensure all edge cases are covered. Hope this helps for now. cheers! |
Thanks so much for looking into this! So how I understood In your test case, you write: unsafe_packages = [f"{package}", "some-other-package-124"]
ignore_packages = unsafe_packages Which does not make sense in my understanding of the code. The final version I came up with was: # If any of these more serious errors are not encountered, the return may
# still be `0` depending on the command line options passed
if not any(
[
bad_unsafe_packages,
missing_unsafe_packages,
unsafe_licenses.found,
]
):
# We are allowing a extra packages and there are extra packages
extra_packages_declared_and_allowed = (
len(unnecessary_unsafe_packages) + len(unnecessary_ignore_packages)
> 0
and "allow_extra_packages" in options
)
....... This makes sense to me. If no packages are |
No problem, thanks for helping to uncover the problems! 😅 So If a package with a license is marked as The problem with
with
So in the Does this make sense to you? |
Thanks for the detailed answer once again. To be entirely honest, I never understood the README when it said "If you rely on a package that does not come with a license you have to explicitly list it as such." regarding the unsafe packages. Regardless, it appeared to me that when a package was listed there and was installed, it would error if it was encountered. Therefore I treated that as a blacklist of packages. Please elaborate on the case that that is not true/correct? Otherwise, let's assume my understanding where unsafe -> blacklist, ignore -> whitelist. We have 3 conditions in the bad_unsafe_packages,
missing_unsafe_packages,
unsafe_licenses.found, My goal, as described in my comment, is that these conditions are considered "serious" errors -- when there is an unsafe package detected, when a package has Now, referring to your truth table for
I only want to enter the For
As a matter of fact, when say only one of the errors Do you follow now the logic for why I believe |
That's so great to know! We need to improve the API and explanations in the readme and cli helpers! The reasoning behind the |
The setup with the unsafe/safe packages/licenses should be such, that there is no need for an explicit blacklist, because any license and or package (without license) is automatically on the blacklist, as anything which is not on one of the whitelists and installed will lead to an error. |
What if a Python package has an incorrect license? The PyPI info was not updated when the |
How is the behavior then different when it is marked as |
Do you mean if the e.g. pypi license classifiers or metadata are different to what LICENSE is actually checked into the repo? So far we trust the license data we get. |
If you install
|
An example of such an
|
I understand finally your whole point regarding What are your thoughts on the |
sorry busy days ... about your longer post above and the
Yes, but just bare in mind that we're already in the error/return code
I agree, but wouldn't this then imply
Yes that's what I tried here
Ah okay I think I now understand what you mean. So you want something along the lines of if not any([packages_are_problem, licenses_are_a_problem, ...]):
return 0 ? Not sure if this would be understandable. Also I think you'd have to wrap this in I think the easiest would be to have this (similar to the code linked above): if any([...]):
...
if package option given and only packages are wrong:
return 0
if license option given and only licenses are wrong:
return 0
if package and license options are given and only packages and licenses are wrong:
return 0
return 1 I think this would cover all the cases and is easier to understand and read compared to these rather unusual boolean transformations. wdyt? |
Hey Sandro. Unrelated to the topic, I am letting you know that I am on holiday for 2 weeks. So I won't be able to run stuff until I get back. Hope that is fine. |
Thanks for letting me know! Enjoy😊👍 |
Hi there. I recently forked your project. I all looks really good. I am going through your source-code to see if it is something that will be actually useful for my use-case. I found these lines: https://github.com/ubersan/pylic/blob/main/pylic/cli/commands/check.py#L89-L98 which deal with handling the
-p
and-l
arguments. This block is very confusing and not intuitive, with theif not all
and then 3 differentif
clauses with various conditions.Would you mind explaining the rationale behind this block? It would be greatly appreciated! I can add a comment in the upstream code once you've explained it to me.
The text was updated successfully, but these errors were encountered: