coconut-develop 3.1.0.post0.dev16__tar.gz → 3.1.1.post0.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/CONTRIBUTING.md +1 -1
  2. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/PKG-INFO +1 -1
  3. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/command/command.py +33 -20
  4. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/command/mypy.py +1 -1
  5. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/command/util.py +13 -4
  6. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/compiler/compiler.py +49 -23
  7. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/compiler/header.py +2 -2
  8. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/constants.py +13 -5
  9. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/root.py +2 -2
  10. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/__main__.py +3 -2
  11. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/main_test.py +4 -1
  12. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/agnostic/primary_1.coco +2 -2
  13. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/agnostic/primary_2.coco +1 -0
  14. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/agnostic/tutorial.coco +4 -2
  15. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/target_2/py2_test.coco +1 -1
  16. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/extras.coco +10 -0
  17. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/DOCS.md +0 -0
  18. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/FAQ.md +0 -0
  19. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/HELP.md +0 -0
  20. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/LICENSE.txt +0 -0
  21. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/MANIFEST.in +0 -0
  22. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/README.rst +0 -0
  23. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/__coconut__/__init__.py +0 -0
  24. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/__coconut__/__init__.pyi +0 -0
  25. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/__coconut__/py.typed +0 -0
  26. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/_coconut/__init__.py +0 -0
  27. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/_coconut/__init__.pyi +0 -0
  28. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/_coconut/py.typed +0 -0
  29. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/__coconut__.py +0 -0
  30. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/__coconut__.pyi +0 -0
  31. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/__init__.py +0 -0
  32. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/__init__.pyi +0 -0
  33. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/__main__.py +0 -0
  34. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/_pyparsing.py +0 -0
  35. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/api.py +0 -0
  36. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/api.pyi +0 -0
  37. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/command/__init__.py +0 -0
  38. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/command/__init__.pyi +0 -0
  39. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/command/cli.py +0 -0
  40. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/command/command.pyi +0 -0
  41. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/command/resources/zcoconut.pth +0 -0
  42. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/command/watch.py +0 -0
  43. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/compiler/__init__.py +0 -0
  44. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/compiler/grammar.py +0 -0
  45. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/compiler/matching.py +0 -0
  46. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/compiler/templates/header.py_template +0 -0
  47. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/compiler/util.py +0 -0
  48. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/convenience.py +0 -0
  49. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/convenience.pyi +0 -0
  50. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/exceptions.py +0 -0
  51. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/highlighter.py +0 -0
  52. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/icoconut/__init__.py +0 -0
  53. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/icoconut/__main__.py +0 -0
  54. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/icoconut/coconut/kernel.json +0 -0
  55. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/icoconut/coconut_py/kernel.json +0 -0
  56. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/icoconut/coconut_py2/kernel.json +0 -0
  57. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/icoconut/coconut_py3/kernel.json +0 -0
  58. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/icoconut/embed.py +0 -0
  59. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/icoconut/root.py +0 -0
  60. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/integrations.py +0 -0
  61. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/main.py +0 -0
  62. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/py.typed +0 -0
  63. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/requirements.py +0 -0
  64. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/terminal.py +0 -0
  65. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/__init__.py +0 -0
  66. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/constants_test.py +0 -0
  67. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/agnostic/__init__.coco +0 -0
  68. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/agnostic/__main__.coco +0 -0
  69. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/agnostic/main.coco +0 -0
  70. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/agnostic/specific.coco +0 -0
  71. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/agnostic/suite.coco +0 -0
  72. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/agnostic/util.coco +0 -0
  73. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/non_strict/non_strict_test.coco +0 -0
  74. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/target_3/py3_test.coco +0 -0
  75. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/target_311/py311_test.coco +0 -0
  76. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/target_35/py35_test.coco +0 -0
  77. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/target_36/py36_test.coco +0 -0
  78. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/target_38/py38_test.coco +0 -0
  79. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/cocotest/target_sys/target_sys_test.coco +0 -0
  80. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/importable.coco +0 -0
  81. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/runnable.coco +0 -0
  82. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/tests/src/runner.coco +0 -0
  83. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut/util.py +0 -0
  84. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/coconut_develop.egg-info/SOURCES.txt +0 -0
  85. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/conf.py +0 -0
  86. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/pyproject.toml +0 -0
  87. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/setup.cfg +0 -0
  88. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/setup.py +0 -0
  89. {coconut-develop-3.1.0.post0.dev16 → coconut-develop-3.1.1.post0.dev2}/xontrib/coconut.py +0 -0
@@ -155,7 +155,7 @@ After you've tested your changes locally, you'll want to add more permanent test
155
155
  1. Preparation:
156
156
  1. Run `make check-reqs` and update dependencies as necessary
157
157
  2. Run `sudo make format`
158
- 3. Make sure `make test`, `make test-py2`, and `make test-easter-eggs` are passing
158
+ 3. Make sure `make test`, `make test-pyright`, and `make test-easter-eggs` are passing
159
159
  4. Ensure that `coconut --watch` can successfully compile files when they're modified
160
160
  5. Check changes in [`compiled-cocotest`](https://github.com/evhub/compiled-cocotest), [`pyprover`](https://github.com/evhub/pyprover), and [`coconut-prelude`](https://github.com/evhub/coconut-prelude)
161
161
  6. Check [Codebeat](https://codebeat.co/a/evhub/projects) and [LGTM](https://lgtm.com/dashboard) for `coconut` and `compiled-cocotest`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: coconut-develop
3
- Version: 3.1.0.post0.dev16
3
+ Version: 3.1.1.post0.dev2
4
4
  Summary: Simple, elegant, Pythonic functional programming.
5
5
  Home-page: http://coconut-lang.org
6
6
  Author: Evan Hubinger
@@ -73,6 +73,7 @@ from coconut.constants import (
73
73
  coconut_cache_dir,
74
74
  coconut_sys_kwargs,
75
75
  interpreter_uses_incremental,
76
+ pyright_config_file,
76
77
  )
77
78
  from coconut.util import (
78
79
  univ_open,
@@ -293,7 +294,6 @@ class Command(object):
293
294
  set_recursion_limit(args.recursion_limit)
294
295
  self.fail_fast = args.fail_fast
295
296
  self.display = args.display
296
- self.pyright = args.pyright
297
297
  self.prompt.vi_mode = args.vi_mode
298
298
  if args.style is not None:
299
299
  self.prompt.set_style(args.style)
@@ -344,9 +344,11 @@ class Command(object):
344
344
  ),
345
345
  )
346
346
 
347
- # process mypy args and print timing info (must come after compiler setup)
347
+ # process mypy + pyright args and print timing info (must come after compiler setup)
348
348
  if args.mypy is not None:
349
349
  self.set_mypy_args(args.mypy)
350
+ if args.pyright:
351
+ self.enable_pyright()
350
352
  logger.log_compiler_stats(self.comp)
351
353
 
352
354
  # do compilation, keeping track of compiled filepaths
@@ -697,15 +699,15 @@ class Command(object):
697
699
  errmsg = format_error(err)
698
700
  else:
699
701
  errmsg = err.__class__.__name__
700
- if errmsg is not None:
701
- if self.errmsg is None:
702
- self.errmsg = errmsg
703
- elif errmsg not in self.errmsg:
704
- if logger.verbose:
705
- self.errmsg += "\nAnd error: " + errmsg
706
- else:
707
- self.errmsg += "; " + errmsg
708
- if code is not None:
702
+ if code:
703
+ if errmsg is not None:
704
+ if self.errmsg is None:
705
+ self.errmsg = errmsg
706
+ elif errmsg not in self.errmsg:
707
+ if logger.verbose:
708
+ self.errmsg += "\nAnd error: " + errmsg
709
+ else:
710
+ self.errmsg += "; " + errmsg
709
711
  self.exit_code = code or self.exit_code
710
712
 
711
713
  @contextmanager
@@ -888,7 +890,7 @@ class Command(object):
888
890
  if no_str_code is not None:
889
891
  result = mypy_builtin_regex.search(no_str_code)
890
892
  if result:
891
- logger.warn("found mypy-only built-in " + repr(result.group(0)) + "; pass --mypy to use mypy-only built-ins at the interpreter")
893
+ logger.warn("found type-checking-only built-in " + repr(result.group(0)) + "; pass --mypy to use such built-ins at the interpreter")
892
894
 
893
895
  else: # header is included
894
896
  compiled = rem_encoding(compiled)
@@ -935,10 +937,11 @@ class Command(object):
935
937
  if mypy_args is None:
936
938
  self.mypy_args = None
937
939
 
938
- elif mypy_install_arg in mypy_args:
940
+ stub_dir = set_mypy_path()
941
+
942
+ if mypy_install_arg in mypy_args:
939
943
  if mypy_args != [mypy_install_arg]:
940
944
  raise CoconutException("'--mypy install' cannot be used alongside other --mypy arguments")
941
- stub_dir = set_mypy_path()
942
945
  logger.show_sig("Successfully installed MyPy stubs into " + repr(stub_dir))
943
946
  self.mypy_args = None
944
947
 
@@ -968,10 +971,15 @@ class Command(object):
968
971
  logger.log("MyPy args:", self.mypy_args)
969
972
  self.mypy_errs = []
970
973
 
974
+ def enable_pyright(self):
975
+ """Enable the use of Pyright for type-checking."""
976
+ update_pyright_config()
977
+ self.pyright = True
978
+
971
979
  def run_type_checking(self, paths=(), code=None):
972
980
  """Run type-checking on the given paths / code."""
973
981
  if self.mypy_args is not None:
974
- set_mypy_path()
982
+ set_mypy_path(ensure_stubs=False)
975
983
  from coconut.command.mypy import mypy_run
976
984
  args = list(paths) + self.mypy_args
977
985
  if code is not None: # interpreter
@@ -996,13 +1004,18 @@ class Command(object):
996
1004
  logger.printerr(line)
997
1005
  self.mypy_errs.append(line)
998
1006
  if self.pyright:
999
- config_file = update_pyright_config()
1000
1007
  if code is not None:
1001
- logger.warn("--pyright only works on files, not code snippets or at the interpreter")
1008
+ logger.warn("--pyright only works on files, not code snippets or at the interpreter (use --mypy instead)")
1002
1009
  if paths:
1003
- from pyright import main
1004
- args = ["--project", config_file, "--pythonversion", self.type_checking_version] + list(paths)
1005
- main(args)
1010
+ try:
1011
+ from pyright import main
1012
+ except ImportError:
1013
+ raise CoconutException(
1014
+ "coconut --pyright requires Pyright",
1015
+ extra="run '{python} -m pip install coconut[pyright]' to fix".format(python=sys.executable),
1016
+ )
1017
+ args = ["--project", pyright_config_file, "--pythonversion", self.type_checking_version] + list(paths)
1018
+ self.register_exit_code(main(args), errmsg="Pyright error")
1006
1019
 
1007
1020
  def run_silent_cmd(self, *args):
1008
1021
  """Same as run_cmd$(show_output=logger.verbose)."""
@@ -34,7 +34,7 @@ try:
34
34
  from mypy.api import run
35
35
  except ImportError:
36
36
  raise CoconutException(
37
- "--mypy flag requires MyPy library",
37
+ "coconut --mypy requires MyPy",
38
38
  extra="run '{python} -m pip install coconut[mypy]' to fix".format(python=sys.executable),
39
39
  )
40
40
 
@@ -488,10 +488,12 @@ def set_env_var(name, value):
488
488
  os.environ[py_str(name)] = py_str(value)
489
489
 
490
490
 
491
- def set_mypy_path():
491
+ def set_mypy_path(ensure_stubs=True):
492
492
  """Put Coconut stubs in MYPYPATH."""
493
+ if ensure_stubs:
494
+ install_stubs()
493
495
  # mypy complains about the path if we don't use / over \
494
- install_dir = install_stubs().replace(os.sep, "/")
496
+ install_dir = installed_stub_dir.replace(os.sep, "/")
495
497
  original = os.getenv(mypy_path_env_var)
496
498
  if original is None:
497
499
  new_mypy_path = install_dir
@@ -507,13 +509,20 @@ def set_mypy_path():
507
509
 
508
510
  def update_pyright_config(python_version=None):
509
511
  """Save an updated pyrightconfig.json."""
512
+ stubs_dir = install_stubs()
510
513
  update_existing = os.path.exists(pyright_config_file)
511
514
  with univ_open(pyright_config_file, "r+" if update_existing else "w") as config_file:
512
515
  if update_existing:
513
- config = readfile(config_file, in_json=True)
516
+ try:
517
+ config = readfile(config_file, in_json=True)
518
+ except ValueError:
519
+ raise CoconutException("invalid JSON syntax in " + repr(pyright_config_file))
514
520
  else:
515
521
  config = extra_pyright_args.copy()
516
- config["extraPaths"] = [install_stubs()]
522
+ if "extraPaths" not in config:
523
+ config["extraPaths"] = []
524
+ if stubs_dir not in config["extraPaths"]:
525
+ config["extraPaths"].append(stubs_dir)
517
526
  if python_version is not None:
518
527
  config["pythonVersion"] = python_version
519
528
  writefile(config_file, config, in_json=True, indent=tabideal)
@@ -246,12 +246,17 @@ else:
246
246
  )
247
247
 
248
248
 
249
- def imported_names(imports):
250
- """Yields all the names imported by imports = [[imp1], [imp2, as], ...]."""
249
+ def get_imported_names(imports):
250
+ """Returns all the names imported by imports = [[imp1], [imp2, as], ...] and whether there is a star import."""
251
+ saw_names = []
252
+ saw_star = False
251
253
  for imp in imports:
252
254
  imp_name = imp[-1].split(".", 1)[0]
253
- if imp_name != "*":
254
- yield imp_name
255
+ if imp_name == "*":
256
+ saw_star = True
257
+ else:
258
+ saw_names.append(imp_name)
259
+ return saw_names, saw_star
255
260
 
256
261
 
257
262
  def special_starred_import_handle(imp_all=False):
@@ -529,7 +534,8 @@ class Compiler(Grammar, pickleable_obj):
529
534
  # but always overwrite temp_vars_by_key since they store locs that will be invalidated
530
535
  self.temp_vars_by_key = {}
531
536
  self.parsing_context = defaultdict(list)
532
- self.unused_imports = defaultdict(list)
537
+ self.name_info = defaultdict(lambda: {"imported": [], "referenced": [], "assigned": []})
538
+ self.star_import = False
533
539
  self.kept_lines = []
534
540
  self.num_lines = 0
535
541
  self.disable_name_check = False
@@ -942,6 +948,11 @@ class Compiler(Grammar, pickleable_obj):
942
948
  if self.strict:
943
949
  raise self.make_err(CoconutStyleError, *args, **kwargs)
944
950
 
951
+ def strict_warn(self, *args, **kwargs):
952
+ internal_assert("extra" not in kwargs, "cannot pass extra=... to strict_warn")
953
+ if self.strict:
954
+ self.syntax_warning(*args, extra="remove --strict to dismiss", **kwargs)
955
+
945
956
  def syntax_warning(self, message, original, loc, **kwargs):
946
957
  """Show a CoconutSyntaxWarning. Usage:
947
958
  self.syntax_warning(message, original, loc)
@@ -1319,21 +1330,30 @@ class Compiler(Grammar, pickleable_obj):
1319
1330
  elif inputstring is not None and not inner:
1320
1331
  logger.log("No streamlining done for input of length {length}.".format(length=input_len))
1321
1332
 
1333
+ def qa_error(self, msg, original, loc):
1334
+ """Strict error or warn an error that should be disabled by a NOQA comment."""
1335
+ ln = self.adjust(lineno(loc, original))
1336
+ comment = self.reformat(" ".join(self.comments[ln]), ignore_errors=True)
1337
+ if not self.noqa_regex.search(comment):
1338
+ self.strict_err_or_warn(
1339
+ msg + " (add '# NOQA' to suppress)",
1340
+ original,
1341
+ loc,
1342
+ endpoint=False,
1343
+ )
1344
+
1322
1345
  def run_final_checks(self, original, keep_state=False):
1323
1346
  """Run post-parsing checks to raise any necessary errors/warnings."""
1324
- # only check for unused imports if we're not keeping state accross parses
1347
+ # only check for unused imports/etc. if we're not keeping state accross parses
1325
1348
  if not keep_state:
1326
- for name, locs in self.unused_imports.items():
1327
- for loc in locs:
1328
- ln = self.adjust(lineno(loc, original))
1329
- comment = self.reformat(" ".join(self.comments[ln]), ignore_errors=True)
1330
- if not self.noqa_regex.search(comment):
1331
- self.strict_err_or_warn(
1332
- "found unused import " + repr(self.reformat(name, ignore_errors=True)) + " (add '# NOQA' to suppress)",
1333
- original,
1334
- loc,
1335
- endpoint=False,
1336
- )
1349
+ for name, info in self.name_info.items():
1350
+ if info["imported"] and not info["referenced"]:
1351
+ for loc in info["imported"]:
1352
+ self.qa_error("found unused import " + repr(self.reformat(name, ignore_errors=True)), original, loc)
1353
+ if not self.star_import: # only check for undefined names when there are no * imports
1354
+ if name not in all_builtins and info["referenced"] and not (info["assigned"] or info["imported"]):
1355
+ for loc in info["referenced"]:
1356
+ self.qa_error("found undefined name " + repr(self.reformat(name, ignore_errors=True)), original, loc)
1337
1357
 
1338
1358
  def parse_line_by_line(self, init_parser, line_parser, original):
1339
1359
  """Apply init_parser then line_parser repeatedly."""
@@ -3731,13 +3751,17 @@ if {store_var} is not _coconut_sentinel:
3731
3751
  else:
3732
3752
  raise CoconutInternalException("invalid import tokens", tokens)
3733
3753
  imports = list(imports)
3734
- if imp_from == "*" or imp_from is None and "*" in imports:
3754
+ imported_names, star_import = get_imported_names(imports)
3755
+ self.star_import = self.star_import or star_import
3756
+ if star_import:
3757
+ self.strict_warn("found * import; these disable Coconut's undefined name detection", original, loc)
3758
+ if imp_from == "*" or (imp_from is None and star_import):
3735
3759
  if not (len(imports) == 1 and imports[0] == "*"):
3736
3760
  raise self.make_err(CoconutSyntaxError, "only [from *] import * allowed, not from * import name", original, loc)
3737
3761
  self.syntax_warning("[from *] import * is a Coconut Easter egg and should not be used in production code", original, loc)
3738
3762
  return special_starred_import_handle(imp_all=bool(imp_from))
3739
- for imp_name in imported_names(imports):
3740
- self.unused_imports[imp_name].append(loc)
3763
+ for imp_name in imported_names:
3764
+ self.name_info[imp_name]["imported"].append(loc)
3741
3765
  return self.universal_import(loc, imports, imp_from=imp_from)
3742
3766
 
3743
3767
  def complex_raise_stmt_handle(self, loc, tokens):
@@ -4575,7 +4599,7 @@ async with {iter_item} as {temp_var}:
4575
4599
  return tokens[0]
4576
4600
  else:
4577
4601
  if not allow_silent_concat:
4578
- self.strict_err_or_warn("found Python-style implicit string concatenation (use explicit '+' instead)", original, loc)
4602
+ self.strict_err_or_warn("found implicit string concatenation (use explicit '+' instead)", original, loc)
4579
4603
  if any(s.endswith(")") for s in tokens): # has .format() calls
4580
4604
  # parens are necessary for string_atom_handle
4581
4605
  return "(" + " + ".join(tokens) + ")"
@@ -4989,8 +5013,10 @@ class {protocol_var}({tokens}, _coconut.typing.Protocol): pass
4989
5013
  )
4990
5014
  return typevars[name]
4991
5015
 
4992
- if not assign:
4993
- self.unused_imports.pop(name, None)
5016
+ if assign:
5017
+ self.name_info[name]["assigned"].append(loc)
5018
+ else:
5019
+ self.name_info[name]["referenced"].append(loc)
4994
5020
 
4995
5021
  if (
4996
5022
  assign
@@ -939,8 +939,8 @@ def getheader(which, use_hash, target, no_tco, strict, no_wrap):
939
939
  header += "from __future__ import print_function, absolute_import, unicode_literals, division\n"
940
940
  # including generator_stop here is fine, even though to universalize generator returns
941
941
  # we raise StopIteration errors, since we only do so when target_info < (3, 3)
942
- elif target_info >= (3, 13):
943
- # 3.13 supports lazy annotations, so we should just use that instead of from __future__ import annotations
942
+ elif target_info >= (3, 14):
943
+ # 3.14 supports lazy annotations, so we should just use that instead of from __future__ import annotations
944
944
  header += "from __future__ import generator_stop\n"
945
945
  elif target_info >= (3, 7):
946
946
  if no_wrap:
@@ -222,6 +222,7 @@ supported_py3_vers = (
222
222
  (3, 11),
223
223
  (3, 12),
224
224
  (3, 13),
225
+ (3, 14),
225
226
  )
226
227
 
227
228
  # must be in ascending order and kept up-to-date with https://devguide.python.org/versions
@@ -233,6 +234,7 @@ py_vers_with_eols = (
233
234
  ("311", dt.datetime(2027, 11, 1)),
234
235
  ("312", dt.datetime(2028, 11, 1)),
235
236
  ("313", dt.datetime(2029, 11, 1)),
237
+ ("314", dt.datetime(2030, 11, 1)),
236
238
  )
237
239
 
238
240
  # must match supported vers above and must be replicated in DOCS
@@ -251,6 +253,7 @@ specific_targets = (
251
253
  "311",
252
254
  "312",
253
255
  "313",
256
+ "314",
254
257
  )
255
258
  pseudo_targets = {
256
259
  "universal": "",
@@ -604,15 +607,20 @@ python_builtins = (
604
607
  "tuple", "type",
605
608
  "vars",
606
609
  "zip",
610
+ 'Ellipsis',
607
611
  "__import__",
608
612
  '__name__',
609
613
  '__file__',
610
614
  '__annotations__',
611
615
  '__debug__',
616
+ '__build_class__',
617
+ '__loader__',
618
+ '__package__',
619
+ '__spec__',
612
620
  )
613
621
 
614
622
  python_exceptions = (
615
- "BaseException", "BaseExceptionGroup", "GeneratorExit", "KeyboardInterrupt", "SystemExit", "Exception", "ArithmeticError", "FloatingPointError", "OverflowError", "ZeroDivisionError", "AssertionError", "AttributeError", "BufferError", "EOFError", "ExceptionGroup", "BaseExceptionGroup", "ImportError", "ModuleNotFoundError", "LookupError", "IndexError", "KeyError", "MemoryError", "NameError", "UnboundLocalError", "OSError", "BlockingIOError", "ChildProcessError", "ConnectionError", "BrokenPipeError", "ConnectionAbortedError", "ConnectionRefusedError", "ConnectionResetError", "FileExistsError", "FileNotFoundError", "InterruptedError", "IsADirectoryError", "NotADirectoryError", "PermissionError", "ProcessLookupError", "TimeoutError", "ReferenceError", "RuntimeError", "NotImplementedError", "RecursionError", "StopAsyncIteration", "StopIteration", "SyntaxError", "IndentationError", "TabError", "SystemError", "TypeError", "ValueError", "UnicodeError", "UnicodeDecodeError", "UnicodeEncodeError", "UnicodeTranslateError", "Warning", "BytesWarning", "DeprecationWarning", "EncodingWarning", "FutureWarning", "ImportWarning", "PendingDeprecationWarning", "ResourceWarning", "RuntimeWarning", "SyntaxWarning", "UnicodeWarning", "UserWarning",
623
+ 'ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BaseExceptionGroup', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'EncodingWarning', 'EnvironmentError', 'Exception', 'ExceptionGroup', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError'
616
624
  )
617
625
 
618
626
  always_keep_parse_name_prefix = "HAS_"
@@ -1043,7 +1051,7 @@ unpinned_min_versions = {
1043
1051
  ("argparse", "py<27"): (1, 4),
1044
1052
  "pexpect": (4,),
1045
1053
  ("trollius", "py<3;cpy"): (2, 2),
1046
- "requests": (2, 31),
1054
+ "requests": (2, 32),
1047
1055
  ("numpy", "py39"): (1, 26),
1048
1056
  ("xarray", "py39"): (2024,),
1049
1057
  ("dataclasses", "py==36"): (0, 8),
@@ -1055,14 +1063,14 @@ unpinned_min_versions = {
1055
1063
  "pyright": (1, 1),
1056
1064
  ("jupyter-console", "py37"): (6, 6),
1057
1065
  ("typing", "py<35"): (3, 10),
1058
- ("typing_extensions", "py>=38"): (4, 11),
1066
+ ("typing_extensions", "py>=38"): (4, 12),
1059
1067
  ("ipykernel", "py38"): (6,),
1060
1068
  ("jedi", "py39"): (0, 19),
1061
- ("pygments", "py>=39"): (2, 17),
1069
+ ("pygments", "py>=39"): (2, 18),
1062
1070
  ("xonsh", "py39"): (0, 16),
1063
1071
  ("async_generator", "py35"): (1, 10),
1064
1072
  ("exceptiongroup", "py37;py<311"): (1,),
1065
- ("ipython", "py>=310"): (8, 24),
1073
+ ("ipython", "py>=310"): (8, 25),
1066
1074
  "py-spy": (0, 3),
1067
1075
  }
1068
1076
 
@@ -23,10 +23,10 @@ import sys as _coconut_sys
23
23
  # VERSION:
24
24
  # -----------------------------------------------------------------------------------------------------------------------
25
25
 
26
- VERSION = "3.1.0"
26
+ VERSION = "3.1.1"
27
27
  VERSION_NAME = None
28
28
  # False for release, int >= 1 for develop
29
- DEVELOP = 16
29
+ DEVELOP = 2
30
30
  ALPHA = False # for pre releases rather than post releases
31
31
 
32
32
  assert DEVELOP is False or DEVELOP >= 1, "DEVELOP must be False or an int >= 1"
@@ -44,12 +44,13 @@ def main(args=None):
44
44
 
45
45
  # compile everything
46
46
  print("Compiling Coconut test suite with args %r and agnostic_target=%r." % (args, agnostic_target))
47
+ type_checking = "--mypy" in args or "--pyright" in args
47
48
  comp_all(
48
49
  args,
49
50
  agnostic_target=agnostic_target,
50
- expect_retcode=0 if "--mypy" not in args else None,
51
+ expect_retcode=0 if not type_checking else None,
51
52
  check_errors="--verbose" not in args,
52
- ignore_output=WINDOWS and "--mypy" not in args,
53
+ ignore_output=WINDOWS and not type_checking,
53
54
  )
54
55
 
55
56
 
@@ -154,6 +154,8 @@ always_err_strs = (
154
154
  ignore_error_lines_with = (
155
155
  # ignore SyntaxWarnings containing assert_raises or raise
156
156
  "raise",
157
+ # ignore Pyright errors
158
+ " - error: ",
157
159
  )
158
160
 
159
161
  mypy_snip = "a: str = count()[0]"
@@ -179,7 +181,8 @@ ignore_last_lines_with = (
179
181
  "from distutils.version import LooseVersion",
180
182
  ": SyntaxWarning: 'int' object is not ",
181
183
  ": CoconutWarning: Deprecated use of ",
182
- " assert_raises(",
184
+ " assert_raises(",
185
+ " assert ",
183
186
  "Populating initial parsing cache",
184
187
  "_coconut.warnings.warn(",
185
188
  ": SyntaxWarning: invalid escape sequence",
@@ -90,7 +90,7 @@ def primary_test_1() -> bool:
90
90
  \\assert data == 3
91
91
  \\def backslash_test():
92
92
  return (x) -> x
93
- assert \(1) == 1 == backslash_test()(1)
93
+ assert \(1) == 1 == backslash_test()(1) # NOQA
94
94
  assert True is (\(
95
95
  "hello"
96
96
  ) == "hello" == \(
@@ -100,7 +100,7 @@ def primary_test_1() -> bool:
100
100
  x,
101
101
  y):
102
102
  return x + y
103
- assert multiline_backslash_test(1, 2) == 3
103
+ assert multiline_backslash_test(1, 2) == 3 # noqa
104
104
  \\ assert True
105
105
  class one_line_class: pass
106
106
  assert isinstance(one_line_class(), one_line_class)
@@ -465,6 +465,7 @@ def primary_test_2() -> bool:
465
465
  "{x}"
466
466
  """ == '\n"2"\n'
467
467
  assert f"\{1}" == "\\1"
468
+ assert f''' '{1}' ''' == " '1' "
468
469
 
469
470
  with process_map.multiple_sequential_calls(): # type: ignore
470
471
  assert map((+), range(3), range(4)$[:-1], strict=True) |> list == [0, 2, 4] == process_map((+), range(3), range(4)$[:-1], strict=True) |> list # type: ignore
@@ -22,13 +22,15 @@ assert range(1, 5) |> product == 24
22
22
  first_five_words = .split() ..> .$[:5] ..> " ".join
23
23
  assert first_five_words("ab cd ef gh ij kl") == "ab cd ef gh ij"
24
24
 
25
- @recursive_iterator
25
+ # TODO: recursive_iterator -> recursive_generator
26
+ @recursive_iterator # noqa
26
27
  def fib() = (1, 1) :: map((+), fib(), fib()$[1:])
27
28
  assert fib()$[:5] |> list == [1, 1, 2, 3, 5]
28
29
 
30
+ # TODO: parallel_map -> process_map
29
31
  # can't use parallel_map here otherwise each process would have to rerun all
30
32
  # the tutorial tests since we don't guard them behind __name__ == "__main__"
31
- assert range(100) |> concurrent_map$(.**2) |> list |> .$[-1] == 9801
33
+ assert range(100) |> thread_map$(.**2) |> list |> .$[-1] == 9801
32
34
 
33
35
  def factorial(n, acc=1):
34
36
  match n:
@@ -4,5 +4,5 @@ def py2_test() -> bool:
4
4
  assert py_map((+)$(2), range(5)) == [2, 3, 4, 5, 6]
5
5
  assert py_range(5) == [0, 1, 2, 3, 4]
6
6
  assert not isinstance(long(1), py_int) # type: ignore
7
- assert py_str(3) == b"3" == unicode(b"3") # type: ignore
7
+ assert py_str(3) == b"3" == unicode(b"3") # noqa # type: ignore
8
8
  return True
@@ -414,6 +414,16 @@ import abc
414
414
  except CoconutStyleError as err:
415
415
  assert str(err) == """found unused import 'abc' (add '# NOQA' to suppress) (remove --strict to downgrade to a warning) (line 1)
416
416
  import abc"""
417
+ try:
418
+ parse("""
419
+ 1
420
+ 2 + x
421
+ 3
422
+ """.strip())
423
+ except CoconutStyleError as err:
424
+ assert str(err) == """found undefined name 'x' (add '# NOQA' to suppress) (remove --strict to downgrade to a warning) (line 2)
425
+ 2 + x
426
+ ^"""
417
427
  assert_raises(-> parse("""
418
428
  class A(object):
419
429
  1