evhub / coconut

Simple, elegant, Pythonic functional programming.
http://coconut-lang.org
Apache License 2.0
4.07k stars 121 forks source link

Changes introduced in 1.3.1.post0.dev21 break build #402

Open ArneBachmann opened 6 years ago

ArneBachmann commented 6 years ago

In my test code I mock user input to simulate interactive console interaction. Since above mentioned version, the tests jump into interactive mode instead of being mocked (vs. .dev20, which I just confirmed to still work).

The code in question is thus:

input builtins
def mockInput(datas:str[], func) -> Any:
  with mock.patch("builtins.input", side_effect = datas): return func()

being used in the test cases as

mockInput(["t"], () -> sos.update("mod", ["--ask-lines"]))

Any changes in the coconut imports that lead to that error?

evhub commented 6 years ago

This looks related to the fix for #400, which was the major issue that was fixed in dev21.

evhub commented 6 years ago

@ArneBachmann The issue is that Coconut now does from builtins import input to resolve #400, which, as per where to patch, means you now need to patch the file where input is used rather than builtins.input where it is defined.

ArneBachmann commented 6 years ago

That was quite something to work around...

# Compute version-number for version-dependent features
try: from typing import *  # we cannot delay this import, since we need to type-check the Coconut version-detection, which again is required to know if we actually can type-check...
except: pass

from coconut.convenience import version as _coco_version  # Compute version-number for version-dependent features
_coco_ver:str[] = _coco_version("num").split(".")[:4]  # Get number string
coco_version:Tuple[int,int,int,int] = (
    int(_coco_ver[0]),
    int(_coco_ver[1]),
    int(_coco_ver[2].split("-")[0] if "-" in _coco_ver[2] else _coco_ver[2]),
    int(_coco_ver[2].split("-")[1].split("_dev")[1]) if "-" in _coco_ver[2] else 0
  )

# Now the actual user code
def mockInput(datas:str[], func: -> Any) -> Any:
    if coco_version < (1, 3, 1, 21):
        import builtins
        with mock.patch("builtins.input", side_effect = datas): return func()
    else:
        with mock.patch("A.B.input", side_effect = datas): return func()

this, however, did succeed only partially. Reason is that in my module A I do an from B import *. For some reason I can only mock A.B.input but not A.input, although both are identical. Must be something in the mock logic, I guess.

I managed to work around it by doing a double import in A:

from A.B import *
import A.B as _B

and then use input in B, but _B.input in A.

If someone comes along saying Python was easy, I'll punch 'em...