Skip to content
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

pip: entry_point: Add support for exit codes #550

Merged
merged 2 commits into from
Nov 10, 2021

Conversation

gibfahn
Copy link
Contributor

@gibfahn gibfahn commented Oct 13, 2021

PR Checklist

Please check if your PR fulfills the following requirements:

  • Does not include precompiled binaries, eg. .par files. See CONTRIBUTING.md for info
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature (please, look at the "Scope of the project" section in the README.md file)
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

What is the current behavior?

Entry point used via the entry_point helper, if a function returning a int, the return int does not become the program return code.

What is the new behavior?

Entry points are expected to return an int and this int is the status code.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

I tried looking for the code in setuptool that generates the shim but did not find it proper. The closest I found was setuptools/command/easy_install.py.

My use case is the sphinx-build entry point that maps to the following code:

def main(argv: List[str] = sys.argv[1:]) -> int:
    # ...

Using pipenv install the following shim gets generated:

#!SOMELOCATION/python
# -*- coding: utf-8 -*-
import re
import sys
from sphinx.cmd.build import main
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(main())

This does forward the error to sys.exit. In the case of rules_python, I am not sure we need the regex bit, but I do know.


Commits (oldest to newest)

bc30a71 pip: entry_point: Add support for exit codes


4a044b5 Add test for entrypoint exit codes


cbe7c1a Update tests for entrypoint exit codes


@google-cla
Copy link

google-cla bot commented Oct 14, 2021

We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and then comment @googlebot I fixed it.. If the bot doesn't comment, it means it doesn't think anything has changed.

ℹ️ Googlers: Go here for more info.

@UebelAndre
Copy link
Contributor

This seems exciting! Any updates here?

Copy link
Contributor

@hrfuller hrfuller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall seems like a reasonable change. There is some fit and finish to do before merging.

@@ -2,10 +2,9 @@ workspace(name = "pip_example")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
local_repository(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't commit this. The integration test runner replaces the url here with a file:// url to a tar of the current version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, thanks, reverted.

srcs = ["test.py"],
deps = [":main"],
)

# For pip dependencies which have entry points, the `entry_point` macro can be
# used from the generated `pip_install` repository to access a runnable binary.
# used from the generated `pip_parse` repository to access a runnable binary.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont think this should change here. This is pip_install/BUILD

@@ -11,10 +11,9 @@ http_archive(
],
)

http_archive(
local_repository(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@@ -68,18 +69,27 @@ compile_pip_requirements(
requirements_txt = "requirements_lock.txt",
)

# Test the use of all pip_parse utilities in a single py_test
# Test the use of all pip_install utilities in a single py_test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the other comment changing the rule name seem wrong. Am I missing something?

@@ -2,10 +2,9 @@ workspace(name = "example_repo")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
local_repository(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@@ -2,10 +2,9 @@ workspace(name = "py_import")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
local_repository(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

if __name__ == "__main__":
from {module} import {method}
{method}()
sys.exit({method}())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how often the convention of returning a numerical exit code holds for scripts in PyPI.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gibfahn What I mean is that we should actually check that this is an int / or at least coercible to an int before calling sys.exit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed replying to this one. Was going to say I considered scanning pypi for packages that return an int, but there didn't seem to be a straightforward way of checking that without some fancy mypy style magic 😁

What I mean is that we should actually check that this is an int / or at least coercible to an int before calling sys.exit.

Yeah okay, let me take a look.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So looking at other similar things, existing Python tooling does not check. I am not sure we can check without a lot of code additions.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't it just is_integer as a runtime check?

>>> 1.5.is_integer()
False
>>> 1.0.is_integer()
True
>>> 1.4142135623730951.is_integer()
False

it's much better to exit 0 then to throw, if some module has a non-int main return value. And someone surely does

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, added that check, please take a look.

Pip doesn't seem to do this, so I'd say chances are probably low that it's in use, but better safe than sorry.

alias(
name = "yamllint",
# If `pkg` and `script` are the same, passing a single string to
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is confusing, there's only one thing below.
do you mean "since pkg and script are the same"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this is wrong, fixed.

),
)

alias(
name = "yamllint",
# If `pkg` and `script` are the same, passing a single string to
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, comment doesn't align with code

if __name__ == "__main__":
from {module} import {method}
{method}()
sys.exit({method}())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't it just is_integer as a runtime check?

>>> 1.5.is_integer()
False
>>> 1.0.is_integer()
True
>>> 1.4142135623730951.is_integer()
False

it's much better to exit 0 then to throw, if some module has a non-int main return value. And someone surely does

Copy link
Contributor

@hrfuller hrfuller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes. LGTM.

@hrfuller hrfuller merged commit 431caac into bazelbuild:main Nov 10, 2021
@gibfahn gibfahn deleted the entry-point-return branch November 11, 2021 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants