Open ChameleonRed opened 2 years ago
Even if I agreed that autocompletion in PyCharm is crucial to make Brython popular, I have no idea how to do it.
Someone developed a project brython_autocomplete to avoid error messages in IDEs when importing the browser module.
There are probably many methods - PyCharm support is crucial will speed up coding 5-10x times - so chance to use brython will increase also 5-10x:
First is to create fake python module like in brython_autocomplete (this works for sure). Second is to create stub *.pyi file using dir() or inspect (no tested but should work). Third is to create semi real/fake python modules which allow unit testing.
I think that second can be easy - not sure what with first.
I will try to do maybe second for private use.
See PyCharm help - this solution works I use it often (not sure what happen if no *.py files). https://www.jetbrains.com/help/pycharm/stubs.html#create-stub-external
For me only problem is that I can not use ast/inspect to generate stubs - I have problems to access code VFS dump some exception which I do not understand currently.
I will prepare some code snippets showing ast bugs - you can judge if this bugs can be removed or another method for stubs should be chosen - for me VFS is secret by now.
I wrote such script to stub and it works: If Brython will support ast or at least dta model for reflection more is possible.
Script is half baked so can be improved.
Structure for stub: stub stub\browser__init__.py stub\browser\document.pyi
Autocompletion works (not with typing but works)!
import browser
from browser import document, html
import inspect
from html import escape
body = document.select('body')[0]
INDENT_STR = ' ' * 4
def stub_function(a, v, indent=0) -> list[str]:
out = []
if hasattr(v, '__name__'):
out.append(f'{INDENT_STR * indent}def {v.__name__}():')
else:
out.append(f'{INDENT_STR * indent}def {a}(...) -> ...')
out.append(f'{INDENT_STR * (indent + 1)}pass')
return out
def stub_class(class_type, indent=0):
out = []
out.append(f'{INDENT_STR * indent}class {class_type.__name__}(...):')
for a in dir(class_type):
print(a)
try:
v = getattr(class_type, a)
except Exception as ex:
print(ex)
continue
if inspect.ismethod(v):
stub_function(a, v, indent + 1)
return out
def stub_module(module) -> list[str]:
out = []
print(type(module))
for a in dir(module):
v = getattr(module, a)
# print(a, dir(v))
if inspect.isfunction(v):
out.extend(stub_function(a, v))
elif inspect.isclass(v):
print(a)
out.extend(stub_class(v))
return out
def main():
out = stub_module(browser.document)
body <= html.PRE('\n'.join(escape(str(x)) for x in out))
if __name__ == '__main__':
main()
I noticed that the online API Reference is already, essentially, a stub file.
That is, using the referenced page, you could write a stub file in the following fashion:
"""
The package browser groups the built-in Brython-specific names and modules
"""
# import ...
def alert(message: str) -> None:
"""
a function that prints the message in a pop-up window. Returns None
"""
def bind(target: DOMNode | str, event: str) -> Callable[..., Any]:
"""
a function used as a decorator for event binding. Cf. section [events](https://brython.info/static_doc/en/events.html).
"""
def confirm(message: str) -> bool:
"""
a function that prints the message in a window, and two buttons (ok/cancel). Returns True if ok, False if cancel
"""
# rest ...
This file could then be redistributed with the Brython PyPI package, in order to provide type hinting to Brython built-in modules.
@PierreQuentel Could you let me know where the documentation can be found? I'm interested in seeing how automatizable this could be.
@Alxe The documentation of the browser module is in this markdown file
That is very crucial to give people *.pyi / stub.
I am telling this story very often.
You drive a car and you find obstacle and somebody tell you that you can workaround - you do it - why not - you are hurry.
Next time you will drive car but you will never choose that way since you need do workaround and not drive to target directly.
API is like ways to achieve targets - better API remove as much obstacles as can :) Some API try to force you drive in specific way but it not works if there are alternatives.
I've created this module https://github.com/Real-Gecko/brython-stubs as a proof of concept.
https://user-images.githubusercontent.com/2231969/167427814-a6bd7517-4352-48dd-9adc-bba081987d92.mp4
Issues I see with this approach:
pyi
files are not generated from brython
but are created manually.browser
to lib
which actually contains only stubs but no code, so possible name conflict with another modules.
Using name browser-stubs
did not work with VSCode imports. Putting stubs to brython
folder did not work, import needed to be brython.browser.
py.typed
within brython
did not work either.Can someone explain how this line works? https://github.com/brython-dev/brython/blob/366f15c009bf44416f24dc626ebd796ddb89bb9a/www/src/Lib/browser/html.py#L1
My mistake, this is a very old file that I forgot to remove. The code of browser.html
is in builtin_modules.js.
BTW I noticed that tests fail with latest commit on master branch.
args = [-26.7, 0.25, -0.25, 1.25, 0.75, -0.75]
expected = [-62.208243223652396,
1.2880225246980772,
1.5895753125511862,
-0.0982718364218127,
0.203280951431295,
1.5757045971498584]
for x, r in zip(args, expected):
assert math.lgamma(x) == r
Not sure what's changed in math
but result is like this now:
Good catch ! I mostly test on Firefox and this test passed, but it failed on Chrome and Edge, probably subtle differences in arithmetic operations. I have replaced assert a == b
by assert math.isclose(a, b)
.
I was running on Firefox too, but on Linux.
Nobody's perfect ;-)
So by crawling source code I came to a conclusion that browser
shall be put into brython
namespace:
from brython.browser import ajax, html
I know that it'll break compatibility with existing projects, but we can try and keep it in both places for some time with some kind of deprecation warning. This way we can create browser
subdir in brython
CLI installation and fill it with .pyi
files to provide type hinting and type checking for brython
in dev environments.
My mistake, this is a very old file that I forgot to remove. The code of
browser.html
is in builtin_modules.js.
I think same is true for ajax.py
, svg.py
, webcomponent.py
and webworker.py
I've created a PR for you to check, any ideas on improving are welcome.
@Real-Gecko
So by crawling source code I came to a conclusion that browser shall be put into brython namespace:
Sorry but I will not accept the radical change from import browser
to from brython import browser
if the reason is that it's the only way that an IDE is able to handle the import (with no guarantee that other IDEs will be happy with PyCharm stubs).
The solution provided by brython_autocomplete looks much better to me.
I think same is true for ajax.py, svg.py, webcomponent.py and webworker.py
webworker.py
is an old implementation of Web Workers, replaced long ago by worker.py
. I have removed it.
These scripts cannot be removed: they import the Javascript modules _ajax.js
, _svg.js
etc.
Sorry but I will not accept the radical change from
import browser
tofrom brython import browser
if the reason is that it's the only way that an IDE is able to handle the import (with no guarantee that other IDEs will be happy with PyCharm stubs).
Then we can try and create a separate module brython-stubs
which will contain browser
folder with .pyi
files, but instead of being handwritten it will be generated from actual Brython source code. And by the way it's not PyCharm stubs it's a standard way of typehinting for Python which is used by IDEs.
Ok, I redid the PR and now we have brython-stubs
submodule that can be put on PyPi I guess. It installs browser
folder which contains .pyi
files for Brython browser at least. However they're incomplete as no JS modules are parsed. Preposition for Typescript remains :D
Few words in defense and acceptance of the using of stubs in general.
Stubs is useful for more than just autocompletion in IDE. Also, important, that stubs allows you to use static code analyzers to find errors, like "mypy" or IDE checkers. Which makes it easier to support growing projects and prevent errors in code even before running any code.
And of course working in IDE will much more convenient to work with typed libraries. I.e. this allows check the number and names of parameters, correct assignments of variable/parameter, validate returns types from functions, and so on.
Stubs only packages in PyPi allowed and well specified. And convenient for use in any IDE, just via installing certain stub
package subset via pip. Examples: pandas-stubs, numpy-stubs. Also, good example is Micropython Plugin - they generate stubs for subset of native API provided only on microcontrollers. In common, stub files and packages easy for use and distribute.
As an idea, it is possible to start from a small repository with at least with partial stubs for basic browser API tied to the Brython
version. It is quite easy to create package for this. Here is my ready for use PoC package for Esp32 Micropython. @Real-Gecko, just for reference.
I'm excited about the Brython
project, thanks to Authors!
If Brython
project will overgrow with types that would be just great!
@Real-Gecko, hats off for the work done on the PoC 🚀🚀🚀
I trust you and @Real-Gecko that this would be useful for people who use IDE checkers or code analysers. It's just that I don't know anything about them, and I am afraid that if I accept the PR, in a few months there will be bug reports or feature requests and I will be on my own to reply.
If you create a project to support stubs I will be happy to mention it in Brython documentation.
I'm tempted to create a brython-stubs library but might need your help in fully understanding the API signatures. Is that something you would be interested in? I already wrote some stubs yesterday, while playing with the tool for the first time ... something in the order of;
class TABLE:
def __le__(self, other: TR) -> TABLE: ...
class TH:
def __init__(self, value: str, **kwargs): ...
def __add__(self, other: TH or list[TH]) -> list[TH]: ...
class TD:
def __init__(self, value: str, **kwargs): ...
def __add__(self, other: TD or list[TD]) -> list[TH]: ...
class TR:
def __init__(self, vals: list[TH or TR]): ...
def __le__(self, other: TH or TD) -> TR: ...
Just noticed this issue now. Have you folks seen this? https://github.com/wapringle/mockbrython
With PyCharm it provides a level of auto completion and suppresses annoying "unresolved reference" PyCharm messages when importing from browser
. I'm not affiliated with this project in any way, but would use it if it was maintained.
Why was this marked as completed? Is there an up to date version somewhere?
I was thinking AI could probably do this pretty easily.
I closed it because there has been no activity on this issue for almost one year, no one seems to have time to invest to solve it, and I don't plan to include the solution in the Brython project itself.
But I can leave it open. If you or someone else publish a working solution I will document it.
I need autocomplete during coding in PyCharm. Python heart is fast coding so it is crucial.
I can not import really module
from browser import document
since no such file - PyCharm will allow autocompletion.I think you can generate *.pyi stubs to allow completion. It can be generated in console. I do not know how to do it but you can.
Without autocompletion coding is very hard for beginners and slow for experts. That is true obstacle to make it popular.
For example I can use brython (it is not productive) but my younger programmers will have very big problems.