Skip to content

Feature request: invoke mypy as a module instead of the binary #52

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

Open
seanf opened this issue Jan 27, 2025 · 9 comments
Open

Feature request: invoke mypy as a module instead of the binary #52

seanf opened this issue Jan 27, 2025 · 9 comments

Comments

@seanf
Copy link
Contributor

seanf commented Jan 27, 2025

Background: I'm trying to use PyCharm+mypy to typecheck solutions for the game Joy of Programming, broadly following this guide. Unfortunately, the embedded Python installation which comes with the game has a few shortcomings. In particular, the scripts in steamapps\common\JOY OF PROGRAMMING\JoyOfProgramming\Content\000_MyContent\External\python-3.10.4-embed-amd64\Scripts, notably mypy.exe, include references to the developer's machine. This doesn't affect the game (I assume), but it means that "script" executables are unusable. (I need to use the embedded Python installation to pick up the exact packages it includes, because the solutions will still be run inside the game.)


It would be great if this plugin had an option to invoke mypy as a module from the Python installation, instead of invoking mypy as an executable.

In other words, instead of doing this:

mypy.exe --follow-imports silent --exclude \.pyi$ myfiletocheck.py

it would have the option do this:

$pythonInterpreter -m mypy --follow-imports silent --exclude \.pyi$ myfiletocheck.py

(where the value for pythonInterpreter would come from the PyCharm project settings - "Python Interpreter", and mypy would probably be better as works.szabope.plugins.mypy.services.MypyPackageUtil#PACKAGE)


Some idle solutioning:

If this were to be an option, it would probably need a new UI setting, perhaps a checkbox "Use mypy module from Python" which would disable the "Path to mypy executable" selector below.

However, it seems possible that invoking mypy as a module would work as a general solution for everyone and no-one would really need to invoke it as an executable. If so, this would avoid the need for a new option, and remove the need for the "Path to mypy executable" setting and the autodetectExecutable feature. But I don't know enough about Python to say one way or the other.

Also, it seems like this may affect the "Install mypy" feature, but I don't know how that works.

@szabope
Copy link
Owner

szabope commented Jan 27, 2025

Hey @seanf

Exactly what I thought. :D

szabope/pylint-pycharm-plugin#5

So I'm working on it, but may take a while. There are a lot of related classes annotated as @Internal, so best to avoid them.

Thank you again for using the plugin and contributing to the repo.

@seanf
Copy link
Contributor Author

seanf commented Jan 27, 2025

@szabope If you think we can go with the "modules for everyone" solution, I could try to tackle it, because I'm pretty good at ripping out UI code. :-)

Otherwise, if you want to start a branch with the UI changes as you want them, I could do the execution logic (not that there would be much to it).

Either way, I might need you to handle the changes to works.szabope.plugins.mypy.services.MypySettings please. I'm not sure what's going on with the configuration file, or how best to add/remove/clean things.

@seanf
Copy link
Contributor Author

seanf commented Jan 27, 2025

@szabope If you think we can go with the "modules for everyone" solution, I could try to tackle it, because I'm pretty good at ripping out UI code. :-)

Otherwise, if you want to start a branch with the UI changes as you want them, I could do the execution logic (not that there would be much to it).

Either way, I might need you to handle the changes to works.szabope.plugins.mypy.services.MypySettings please. I'm not sure what's going on with the configuration file, or how best to add/remove/clean things.

On second thought, if it's the "modules for everyone" approach, I probably can take a stab at removing things from the settings. You can always review to see if it's misguided.

Now that you mention it, I'm not really sure how to interpret @ApiStatus.Internal in this project. Normally that would just mean "don't use this from outside", wouldn't it? But this isn't a library.

Anyway, I might give the simplistic "modules for everyone" approach a try, if only for my own use, and see how far I get. And if I fail miserably I might understand szabope/pylint-pycharm-plugin#5 a little better!

@szabope
Copy link
Owner

szabope commented Jan 27, 2025

@Internal as "subject to change without notice" here refers to intellij-platform. e.g. com.intellij.execution.actions.ExecutorAction com.intellij.execution.ExecutorRegistryImpl (RunnerHelper.runSubProcess is interesting here)

I left off at playing around with com.jetbrains.python.run.PythonRunConfiguration and com.intellij.execution.ProgramRunnerUtil.executeConfigurationAsync a couple of days ago (it's just sandboxing for now). What I'm trying to do is to create a run configuration that is not shown in the dropdown, can be configured with any of the registered SDKs including all their IJ related configuration like PYTHONPATH, and can be started from the code at will.
This might help if you want to try solving the case yourself.

I wouldn't remove anything until we have a working PoC for the alternative way of executing mypy, so that we see where we are going.

If you only need to run mypy as a module and is urgent, you probably don't need all this fuss. As a quick fix only for myself I'd remove validation from MypySettings.validateExecutable, use it to set the path to interpreter and change MypyService.buildCommand to insert -m mypy right after the executable.
I haven't tried it, just an idea to save on effort and still have a working solution, even if it's a little ugly.

About szabope/pylint-pycharm-plugin#5 : pylint plugin shares most of its code with mypy plugin. I wanted to let them change independently, and if they seem to converge, I'll extract what's common for them.

@seanf
Copy link
Contributor Author

seanf commented Jan 28, 2025

Thanks @szabope. I don't think I'm going to get anywhere quickly. Turns out this embedded installation only has mypy 1.5.1, which doesn't support options like --output json...

@szabope
Copy link
Owner

szabope commented Jan 28, 2025

I'm making a progress with RunConfiguration-related approach. I'll let you know when I see it coming together.
Roberto's plugin has support for older versions of mypy https://github.com/leinardi/mypy-pycharm/ but it's going out of business.
If you have the free time and motivation you could add support for mypy's default output (and by that for older versions at the same time), but be aware that mypy happens to write some errors to stdout that don't follow the format. (other times it uses stderr and return status does not follow the POSIX-way i.e. != 0 is error)

@seanf
Copy link
Contributor Author

seanf commented Jan 28, 2025

Thanks @szabope. I'm not sure I'll be picking that up, but if I do I'll refer to these in the old plugin (along with works.szabope.plugins.mypy.services.parser.MypyOutputParser):

@szabope
Copy link
Owner

szabope commented Jan 28, 2025

although this does at least seem consistent with "0 is success, anything else is an error/problem"

I don't think it does. A non-zero exit code should indicate that the application failed to fulfill its purpose on some way. Finding a problem in the input and outputting it to stdout for an application whose very purpose is finding problems in the input and outputting it to stdout should not qualify as an error. I find this a common point of misunderstanding.

Pylint introduced --exit-zero switch to comply with this practice.

But - as always - I might be wrong and can be convinced through logical reasoning.

@seanf
Copy link
Contributor Author

seanf commented Jan 29, 2025

although this does at least seem consistent with "0 is success, anything else is an error/problem"

I don't think it does. A non-zero exit code should indicate that the application failed to fulfill its purpose on some way. Finding a problem in the input and outputting it to stdout for an application whose very purpose is finding problems in the input and outputting it to stdout should not qualify as an error. I find this a common point of misunderstanding.

Pylint introduced --exit-zero switch to comply with this practice.

But - as always - I might be wrong and can be convinced through logical reasoning.

Hmm, I suppose. Depends on your definition of error/problem, but I can see what you mean.

In any case, I guess we'll have to deal with (or perhaps bypass) mypy's interpretation of exit codes, especially if the plan is to support older/existing versions of mypy (which don't have --exit-zero). From what I could see, this should be less of a problem with a line-based parser, since it would just depend on having valid (error|warning|note): lines, whereas the JSON version is thrown off by the non-JSON failure messages breaking the JSON structure. You just can't trust mypy to output valid JSON, as I understand it, especially when there are problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants