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

Prefer using the module loader to get source in builder. #1207

Merged
merged 4 commits into from
Dec 15, 2021

Conversation

gpshead
Copy link
Contributor

@gpshead gpshead commented Oct 7, 2021

Steps

  • For new features or bug fixes, add a ChangeLog entry describing what your PR does.
  • Write a good description on what the PR does.

Description

Prefer the module loader get_source() method in AstroidBuilder's module_build() when possible to avoid assumptions about source code being available on a filesystem. Otherwise the source cannot be found and application behavior changes when running within an embedded hermetic interpreter environment (pyoxidizer, etc.).

Without this change, pylint packaged up as such would fail to infer that functions like bz2.compress returned
something other than None, triggering assignment-from-no-return when it clearly should not.

At the pylint level, this works as an application integration regression test - when you run the test build within such a no-filesystem environment.

    def test_safe_infer_on_bz2_compress_return(self):
        src_node = astroid.extract_node('import bz2\nunused = bz2.compress(b"")')
        assignment = next(src_node.nodes_of_class(astroid.Assign))
        inferred = pylint.checkers.utils.safe_infer(assignment.value.func)
        self.assertIsInstance(inferred, astroid.FunctionDef, msg=str(inferred))
        try:
            next(inferred.nodes_of_class(astroid.Return))
        except StopIteration:
            self.fail(f'No return found in body of:\n{str(inferred)}.')

I did not try to come up with a smaller targeted astroid unittest faking the "The path in .__file__ does not exist" environment. That would probably be useful.

Type of Changes

Type
🐛 Bug fix

Related Issue

Otherwise we fail to get the module source code when running in an
embedded hermetic interpreter environment where the source does not
live on a filesystem.
@Pierre-Sassoulas Pierre-Sassoulas added the Enhancement ✨ Improvement to a component label Oct 9, 2021
@Pierre-Sassoulas Pierre-Sassoulas added this to the 2.8.3 milestone Oct 9, 2021
@cdce8p cdce8p added the pylint-tested PRs that don't cause major regressions with pylint label Oct 12, 2021
Copy link
Member

@Pierre-Sassoulas Pierre-Sassoulas left a comment

Choose a reason for hiding this comment

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

If I understand well the testing is easier on pylint ? Is there a simple way to also test that on astroid ?

@Pierre-Sassoulas
Copy link
Member

Hey @gpshead, I'd like to reproduce a fail in the test I just added in pylint here, do you have an idea on how to do that ?

embedded hermetic interpreter environment (pyoxidizer, etc.).

Could we create such a testing environment in github actions ?

@Pierre-Sassoulas Pierre-Sassoulas modified the milestones: 2.8.4, 2.8.5 Oct 25, 2021
@gpshead
Copy link
Contributor Author

gpshead commented Oct 26, 2021

Try this:

  1. Create a venv.
  2. create a littletest.py module with a function who's return value astroid can correctly infer given the source code.
  3. use the py_compile module on that to produce a .pyc file for the module...
  4. Manually install that .pyc file in sys.path somewhere ${your_venv}/lib/pythonX.Y/site-packages/littletest.pyc works, but using your own tempdir you temporarily add to sys.path for this test is likely a better idea.
  5. import littletest in your test code, assert that it's littletest.__loader__.get_source("littletest") returns None (as it should be a SourcelessFileLoader).
  6. monkey patch littletest.__loader__.get_source("littletest") (mock.patch, etc) to return the actual not-in-sys.path str source code to the module.
  7. use test code similar to that PR or what is listed above that imports and uses a function from littletest. Without the fix, presumably it fails as described? With the fix it gets the source and does correct inference?

I didn't try this, but I believe it'd work and be self contained and not have additional dependencies.

ChangeLog Outdated Show resolved Hide resolved
Copy link
Member

@Pierre-Sassoulas Pierre-Sassoulas left a comment

Choose a reason for hiding this comment

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

Merging without tests as pylint's test suite is passing and the actual testing will be done in pylint-dev/pylint#5341

Congratulation on becoming an astroid contributor :)

@Pierre-Sassoulas Pierre-Sassoulas merged commit 6655741 into pylint-dev:main Dec 15, 2021
@gpshead
Copy link
Contributor Author

gpshead commented Dec 15, 2021

Thanks! I had stalled on responding while pondering how to construct a meaningful regression test case without being overcomplicated! :)

@gpshead gpshead deleted the use_loader_get_source branch December 15, 2021 20:07
@Pierre-Sassoulas
Copy link
Member

You're welcome to contribute to pylint-dev/pylint#5341 if you have new ideas :)

Pierre-Sassoulas added a commit to pylint-dev/pylint that referenced this pull request Dec 18, 2021
tushar-deepsource pushed a commit to tushar-deepsource/astroid that referenced this pull request Dec 20, 2021
…1207)

* Use loader.get_source() in builder.module_build()

Otherwise we fail to get the module source code when running in an
embedded hermetic interpreter environment where the source does not
live on a filesystem.

Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Pierre-Sassoulas added a commit to pylint-dev/pylint that referenced this pull request Dec 31, 2021
jacobtylerwalls pushed a commit to jacobtylerwalls/astroid that referenced this pull request Feb 9, 2022
…1207)

* Use loader.get_source() in builder.module_build()

Otherwise we fail to get the module source code when running in an
embedded hermetic interpreter environment where the source does not
live on a filesystem.

Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement ✨ Improvement to a component pylint-tested PRs that don't cause major regressions with pylint
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants