coconut-develop 3.1.2.post0.dev11__tar.gz → 3.2.0.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 (92) hide show
  1. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/.claude/CLAUDE.md +14 -0
  2. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/.claude/settings.local.json +10 -3
  3. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/DOCS.md +70 -0
  4. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/HELP.md +2 -2
  5. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/PKG-INFO +3 -26
  6. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/README.rst +1 -3
  7. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/compiler/compiler.py +48 -11
  8. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/compiler/grammar.py +8 -2
  9. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/compiler/header.py +0 -11
  10. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/compiler/templates/header.py_template +4 -1
  11. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/compiler/util.py +8 -2
  12. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/constants.py +4 -2
  13. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/root.py +14 -2
  14. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/main_test.py +5 -3
  15. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/agnostic/primary_2.coco +80 -4
  16. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/extras.coco +14 -2
  17. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/CONTRIBUTING.md +0 -0
  18. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/FAQ.md +0 -0
  19. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/LICENSE.txt +0 -0
  20. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/MANIFEST.in +0 -0
  21. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/__coconut__/__init__.py +0 -0
  22. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/__coconut__/__init__.pyi +0 -0
  23. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/__coconut__/py.typed +0 -0
  24. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/_coconut/__init__.py +0 -0
  25. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/_coconut/__init__.pyi +0 -0
  26. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/_coconut/py.typed +0 -0
  27. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/__coconut__.py +0 -0
  28. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/__coconut__.pyi +0 -0
  29. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/__init__.py +0 -0
  30. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/__init__.pyi +0 -0
  31. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/__main__.py +0 -0
  32. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/_pyparsing.py +0 -0
  33. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/api.py +0 -0
  34. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/api.pyi +0 -0
  35. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/command/__init__.py +0 -0
  36. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/command/__init__.pyi +0 -0
  37. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/command/cli.py +0 -0
  38. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/command/command.py +0 -0
  39. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/command/command.pyi +0 -0
  40. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/command/mypy.py +0 -0
  41. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/command/resources/zcoconut.pth +0 -0
  42. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/command/util.py +0 -0
  43. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/command/watch.py +0 -0
  44. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/compiler/__init__.py +0 -0
  45. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/compiler/matching.py +0 -0
  46. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/convenience.py +0 -0
  47. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/convenience.pyi +0 -0
  48. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/exceptions.py +0 -0
  49. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/highlighter.py +0 -0
  50. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/icoconut/__init__.py +0 -0
  51. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/icoconut/__main__.py +0 -0
  52. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/icoconut/coconut/kernel.json +0 -0
  53. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/icoconut/coconut_py/kernel.json +0 -0
  54. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/icoconut/coconut_py2/kernel.json +0 -0
  55. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/icoconut/coconut_py3/kernel.json +0 -0
  56. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/icoconut/embed.py +0 -0
  57. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/icoconut/root.py +0 -0
  58. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/integrations.py +0 -0
  59. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/main.py +0 -0
  60. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/py.typed +0 -0
  61. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/requirements.py +0 -0
  62. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/terminal.py +0 -0
  63. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/__init__.py +0 -0
  64. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/__main__.py +0 -0
  65. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/constants_test.py +0 -0
  66. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/agnostic/__init__.coco +0 -0
  67. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/agnostic/__main__.coco +0 -0
  68. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/agnostic/main.coco +0 -0
  69. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/agnostic/primary_1.coco +0 -0
  70. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/agnostic/specific.coco +0 -0
  71. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/agnostic/suite.coco +0 -0
  72. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/agnostic/tutorial.coco +0 -0
  73. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/agnostic/util.coco +0 -0
  74. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/non_strict/non_strict_test.coco +0 -0
  75. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/target_2/py2_test.coco +0 -0
  76. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/target_3/py3_test.coco +0 -0
  77. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/target_311/py311_test.coco +0 -0
  78. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/target_314/py314_test.coco +0 -0
  79. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/target_35/py35_test.coco +0 -0
  80. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/target_36/py36_test.coco +0 -0
  81. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/target_38/py38_test.coco +0 -0
  82. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/cocotest/target_sys/target_sys_test.coco +0 -0
  83. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/importable.coco +0 -0
  84. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/runnable.coco +0 -0
  85. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/tests/src/runner.coco +0 -0
  86. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut/util.py +0 -0
  87. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/coconut_develop.egg-info/SOURCES.txt +0 -0
  88. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/conf.py +0 -0
  89. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/pyproject.toml +0 -0
  90. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/setup.cfg +0 -0
  91. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/setup.py +0 -0
  92. {coconut_develop-3.1.2.post0.dev11 → coconut_develop-3.2.0.post0.dev2}/xontrib/coconut.py +0 -0
@@ -75,3 +75,17 @@ To add tests: write Coconut code with `assert` statements in the appropriate `.c
75
75
  ## Branches
76
76
  - `master` - Stable releases
77
77
  - `develop` - Active development (default branch for contributions)
78
+
79
+ ## Debugging GitHub Actions
80
+
81
+ ### Viewing Logs for a Specific Job
82
+
83
+ When investigating a failed GitHub Actions job, use the GitHub API directly:
84
+
85
+ ```bash
86
+ gh api repos/evhub/coconut/actions/jobs/{job_id}/logs
87
+ ```
88
+
89
+ Replace `{job_id}` with the job ID from the URL (e.g., for `.../job/59132870840`, use `59132870840`).
90
+
91
+ **Note:** The more common `gh run view {run_id} --log-failed` command doesn't work if the overall run is still in progress, even if the specific job you want has already completed. Using the API directly bypasses this limitation.
@@ -1,18 +1,25 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
+ "WebSearch",
4
5
  "Bash(cat:*)",
6
+ "Bash(cd:*)",
7
+ "Bash(ls:*)",
8
+ "Bash(echo:*)",
9
+ "Bash(grep:*)",
10
+ "Bash(find:*)",
5
11
  "Bash(python:*)",
6
- "Bash(xonsh:*)",
7
12
  "Bash(gh:*)",
8
13
  "Bash(pip:*)",
14
+ "Bash(conda:*)",
15
+ "Bash(xonsh:*)",
9
16
  "Bash(make test-tests:*)",
10
- "WebSearch",
11
17
  "WebFetch(domain:coconut.readthedocs.io)",
12
18
  "WebFetch(domain:github.com)",
13
19
  "WebFetch(domain:peps.python.org)",
14
20
  "WebFetch(domain:pypi.org)",
15
- "WebFetch(domain:discuss.python.org)"
21
+ "WebFetch(domain:discuss.python.org)",
22
+ "WebFetch(domain:doc.pypy.org)"
16
23
  ]
17
24
  }
18
25
  }
@@ -1593,6 +1593,76 @@ data namedpt(name `isinstance` str, x `isinstance` int, y `isinstance` int):
1593
1593
  _Can't be done without a series of method definitions for each data type. See the compiled code for the Python syntax._
1594
1594
 
1595
1595
 
1596
+ ### `final`
1597
+
1598
+ Coconut supports `final` declarations to mark variables as constant, preventing reassignment within the same scope. The syntax is:
1599
+ ```coconut
1600
+ final <name> = <value>
1601
+ ```
1602
+
1603
+ Once a variable is declared `final`, it cannot be reassigned within that scope. Attempting to reassign a final variable will result in a compile-time error.
1604
+
1605
+ #### Shadowing
1606
+
1607
+ Inner scopes can freely shadow final variables from outer scopes, creating new bindings:
1608
+ ```coconut
1609
+ final x = 1
1610
+ def f():
1611
+ x = 2 # OK - creates new binding in f's scope, doesn't modify outer x
1612
+ x = 3 # OK - x is not final in this scope
1613
+ return x
1614
+ ```
1615
+
1616
+ You can also explicitly make the inner binding final:
1617
+ ```coconut
1618
+ final x = 1
1619
+ def f():
1620
+ final x = 2 # OK - new final binding that shadows outer x
1621
+ return x
1622
+ ```
1623
+
1624
+ #### Usage Contexts
1625
+
1626
+ The `final` keyword can be used in:
1627
+ - Variable assignments: `final x = 1`
1628
+ - Typed assignments: `final x: int = 1`
1629
+ - Function parameters: `def f(final x): ...`
1630
+ - For loops: `for final i in items: ...`
1631
+ - Match patterns: `case final x: ...`
1632
+
1633
+ #### Escape Hatch
1634
+
1635
+ To bypass final checking (e.g., for metaprogramming), prefix the variable name with a backslash:
1636
+ ```coconut
1637
+ final x = 1
1638
+ \x = 2 # OK - bypasses final check
1639
+ ```
1640
+
1641
+ ##### Example
1642
+
1643
+ **Coconut:**
1644
+ ```coconut
1645
+ final PI = 3.14159
1646
+ final GREETING = "Hello"
1647
+
1648
+ def calculate_area(final radius):
1649
+ return PI * radius ** 2
1650
+
1651
+ # PI = 3.14 # ERROR - cannot reassign final variable 'PI'
1652
+ ```
1653
+
1654
+ **Python:**
1655
+ ```python
1656
+ PI = 3.14159
1657
+ GREETING = "Hello"
1658
+
1659
+ def calculate_area(radius):
1660
+ return PI * radius ** 2
1661
+
1662
+ # No compile-time enforcement - reassignment would be allowed
1663
+ ```
1664
+
1665
+
1596
1666
  ### `where`
1597
1667
 
1598
1668
  Coconut's `where` statement is fairly straightforward. The syntax for a `where` statement is just
@@ -58,7 +58,7 @@ coconut -h
58
58
  ```
59
59
  which should display Coconut's command-line help.
60
60
 
61
- _Note: If you're having trouble, or if anything mentioned in this tutorial doesn't seem to work for you, feel free to [ask for help on Gitter](https://gitter.im/evhub/coconut) and somebody will try to answer your question as soon as possible._
61
+ _Note: If you're having trouble, or if anything mentioned in this tutorial doesn't seem to work for you, feel free to [raise an issue on GitHub](https://github.com/evhub/coconut/issues/new)!_
62
62
 
63
63
  ## Starting Out
64
64
 
@@ -1163,6 +1163,6 @@ def int_map(
1163
1163
 
1164
1164
  And that's it for this tutorial! But that's hardly it for Coconut. All of the features examined in this tutorial, as well as a bunch of others, are detailed in Coconut's [documentation](./DOCS.md).
1165
1165
 
1166
- Also, if you have any other questions not covered in this tutorial, feel free to ask around at Coconut's [Gitter](https://gitter.im/evhub/coconut), a GitHub-integrated chat room for Coconut developers.
1166
+ Also, if you have any other questions not covered in this tutorial, feel free to [raise an issue on GitHub](https://github.com/evhub/coconut/issues/new)!
1167
1167
 
1168
1168
  Finally, Coconut is a new, growing language, and if you'd like to get involved in the development of Coconut, all the code is available completely open-source on Coconut's [GitHub](https://github.com/evhub/coconut). Contributing is a simple as forking the code, making your changes, and proposing a pull request! See Coconuts [contributing guidelines](./CONTRIBUTING.md) for more information.
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coconut-develop
3
- Version: 3.1.2.post0.dev11
3
+ Version: 3.2.0.post0.dev2
4
4
  Summary: Simple, elegant, Pythonic functional programming.
5
5
  Home-page: http://coconut-lang.org
6
6
  Author: Evan Hubinger
7
7
  Author-email: evanjhub@gmail.com
8
8
  License: Apache-2.0
9
- Keywords: functional,programming,language,compiler,pattern,pattern-matching,algebraic,data type,data types,lambda,lambdas,lazy,evaluation,lazy list,lazy lists,tail,recursion,call,recursive,recursive_iterator,infix,function,composition,compose,partial,application,currying,curry,pipeline,pipe,unicode,operator,operators,frozenset,literal,syntax,destructuring,assignment,fold,datamaker,prepattern,iterator,generator,none,coalesce,coalescing,statement,lru_cache,memoization,backport,typing,embed,PEP 622,overrides,islice,itertools,functools,TYPE_CHECKING,Expected,breakpoint,help,reduce,takewhile,dropwhile,tee,count,makedata,consume,process_map,thread_map,addpattern,recursive_generator,fmap,starmap,reiterable,scan,groupsof,memoize,zip_longest,override,flatten,ident,call,safe_call,flip,const,lift,lift_apart,all_equal,collectby,mapreduce,multi_enumerate,cartesian_product,multiset,cycle,windowsof,and_then,and_then_await,async_map,py_chr,py_dict,py_hex,py_input,py_int,py_map,py_object,py_oct,py_open,py_print,py_range,py_str,py_super,py_zip,py_filter,py_reversed,py_enumerate,py_raw_input,py_xrange,py_repr,py_breakpoint,py_min,py_max,_namedtuple_of,reveal_type,reveal_locals,MatchError,CoconutWarning,__fmap__,__iter_getitem__,data,match,case,cases,where,addpattern,then,operator,type,copyclosure,λ
9
+ Keywords: functional,programming,language,compiler,pattern,pattern-matching,algebraic,data type,data types,lambda,lambdas,lazy,evaluation,lazy list,lazy lists,tail,recursion,call,recursive,recursive_iterator,infix,function,composition,compose,partial,application,currying,curry,pipeline,pipe,unicode,operator,operators,frozenset,literal,syntax,destructuring,assignment,fold,datamaker,prepattern,iterator,generator,none,coalesce,coalescing,statement,lru_cache,memoization,backport,typing,embed,PEP 622,overrides,islice,itertools,functools,TYPE_CHECKING,Expected,breakpoint,help,reduce,takewhile,dropwhile,tee,count,makedata,consume,process_map,thread_map,addpattern,recursive_generator,fmap,starmap,reiterable,scan,groupsof,memoize,zip_longest,override,flatten,ident,call,safe_call,flip,const,lift,lift_apart,all_equal,collectby,mapreduce,multi_enumerate,cartesian_product,multiset,cycle,windowsof,and_then,and_then_await,async_map,py_chr,py_dict,py_hex,py_input,py_int,py_map,py_object,py_oct,py_open,py_print,py_range,py_str,py_super,py_zip,py_filter,py_reversed,py_enumerate,py_raw_input,py_xrange,py_repr,py_breakpoint,py_min,py_max,_namedtuple_of,reveal_type,reveal_locals,MatchError,CoconutWarning,__fmap__,__iter_getitem__,data,match,case,cases,where,final,addpattern,then,operator,type,copyclosure,λ
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: Topic :: Software Development
@@ -220,27 +220,6 @@ Requires-Dist: pytest>=7; (python_version >= "3.6" and python_version < "3.8") a
220
220
  Requires-Dist: pytest<8.1,>=8.0; python_version >= "3.8" and extra == "tests"
221
221
  Requires-Dist: pexpect>=4; extra == "tests"
222
222
  Requires-Dist: pytest_remotedata>=0.3; extra == "tests"
223
- Requires-Dist: ipython>=5.4; python_version < "3" and extra == "tests"
224
- Requires-Dist: ipython<7.10,>=7.9; (python_version >= "3" and python_version < "3.7") and extra == "tests"
225
- Requires-Dist: ipython>=7.34; (python_version >= "3.7" and python_version < "3.8") and extra == "tests"
226
- Requires-Dist: ipython>=8.12; (python_version >= "3.8" and python_version < "3.9") and extra == "tests"
227
- Requires-Dist: ipython>=8.18; (python_version >= "3.9" and python_version < "3.11") and extra == "tests"
228
- Requires-Dist: ipython>=9; python_version >= "3.11" and extra == "tests"
229
- Requires-Dist: ipykernel>=4.10; python_version < "3" and extra == "tests"
230
- Requires-Dist: ipykernel>=5.5; (python_version >= "3" and python_version < "3.8") and extra == "tests"
231
- Requires-Dist: ipykernel>=6; (python_version >= "3.8" and python_version < "3.10") and extra == "tests"
232
- Requires-Dist: ipykernel>=7; python_version >= "3.10" and extra == "tests"
233
- Requires-Dist: jupyter-client>=5.3; python_version < "3.5" and extra == "tests"
234
- Requires-Dist: jupyter-client<6.1.13,>=6.1.12; (python_version >= "3.5" and python_version < "3.6") and extra == "tests"
235
- Requires-Dist: jupyter-client>=7.1.2; python_version >= "3.6" and extra == "tests"
236
- Requires-Dist: jedi<0.18,>=0.17; python_version < "3.9" and extra == "tests"
237
- Requires-Dist: jedi>=0.19; python_version >= "3.9" and extra == "tests"
238
- Requires-Dist: pywinpty<0.6,>=0.5; (python_version < "3" and os_name == "nt") and extra == "tests"
239
- Requires-Dist: jupyter>=1.1; extra == "tests"
240
- Requires-Dist: jupyter-console>=5.2; python_version < "3.5" and extra == "tests"
241
- Requires-Dist: jupyter-console>=6.1; (python_version >= "3.5" and python_version < "3.7") and extra == "tests"
242
- Requires-Dist: jupyter-console>=6.6; python_version >= "3.7" and extra == "tests"
243
- Requires-Dist: papermill>=1.2; extra == "tests"
244
223
  Provides-Extra: dev
245
224
  Requires-Dist: ipython>=5.4; python_version < "3" and extra == "dev"
246
225
  Requires-Dist: ipython<7.10,>=7.9; (python_version >= "3" and python_version < "3.7") and extra == "dev"
@@ -364,8 +343,7 @@ To help you get started, check out these links for more information about Coconu
364
343
  - Documentation_: If you're looking for info about a specific feature, check out Coconut's **documentation**.
365
344
  - `Online Interpreter`_: If you want to try Coconut in your browser, check out Coconut's **online interpreter**.
366
345
  - FAQ_: If you have general questions about Coconut—like who Coconut is built for and whether or not you should use it—Coconut's frequently asked questions are often the best place to start.
367
- - `Create a New Issue <https://github.com/evhub/coconut/issues/new>`_: If you're having a problem with Coconut, creating a new issue detailing the problem will allow it to be addressed as soon as possible.
368
- - Gitter_: For any questions, concerns, or comments about anything Coconut-related, ask around at Coconut's Gitter, a GitHub-integrated chat room for Coconut developers.
346
+ - `Create a New Issue <https://github.com/evhub/coconut/issues/new>`_: If you're having a problem with Coconut, creating a new issue detailing the problem will allow it to be addressed as soon as possible. Creating a new issue is also absolutely welcome as a way to ask any questions you might have about Coconut.
369
347
  - Releases_: Want to know what's been added in recent Coconut versions? Check out the release log for all the new features and fixes.
370
348
 
371
349
  .. _Python: https://www.python.org/
@@ -375,7 +353,6 @@ To help you get started, check out these links for more information about Coconu
375
353
  .. _`Online Interpreter`: https://cs121-team-panda.github.io/coconut-interpreter
376
354
  .. _FAQ: http://coconut.readthedocs.io/en/latest/FAQ.html
377
355
  .. _GitHub: https://github.com/evhub/coconut
378
- .. _Gitter: https://gitter.im/evhub/coconut
379
356
  .. _Releases: https://github.com/evhub/coconut/releases
380
357
 
381
358
  Credits
@@ -31,8 +31,7 @@ To help you get started, check out these links for more information about Coconu
31
31
  - Documentation_: If you're looking for info about a specific feature, check out Coconut's **documentation**.
32
32
  - `Online Interpreter`_: If you want to try Coconut in your browser, check out Coconut's **online interpreter**.
33
33
  - FAQ_: If you have general questions about Coconut—like who Coconut is built for and whether or not you should use it—Coconut's frequently asked questions are often the best place to start.
34
- - `Create a New Issue <https://github.com/evhub/coconut/issues/new>`_: If you're having a problem with Coconut, creating a new issue detailing the problem will allow it to be addressed as soon as possible.
35
- - Gitter_: For any questions, concerns, or comments about anything Coconut-related, ask around at Coconut's Gitter, a GitHub-integrated chat room for Coconut developers.
34
+ - `Create a New Issue <https://github.com/evhub/coconut/issues/new>`_: If you're having a problem with Coconut, creating a new issue detailing the problem will allow it to be addressed as soon as possible. Creating a new issue is also absolutely welcome as a way to ask any questions you might have about Coconut.
36
35
  - Releases_: Want to know what's been added in recent Coconut versions? Check out the release log for all the new features and fixes.
37
36
 
38
37
  .. _Python: https://www.python.org/
@@ -42,7 +41,6 @@ To help you get started, check out these links for more information about Coconu
42
41
  .. _`Online Interpreter`: https://cs121-team-panda.github.io/coconut-interpreter
43
42
  .. _FAQ: http://coconut.readthedocs.io/en/latest/FAQ.html
44
43
  .. _GitHub: https://github.com/evhub/coconut
45
- .. _Gitter: https://gitter.im/evhub/coconut
46
44
  .. _Releases: https://github.com/evhub/coconut/releases
47
45
 
48
46
  Credits
@@ -604,6 +604,7 @@ class Compiler(Grammar, pickleable_obj):
604
604
  # but always overwrite temp_vars_by_key since they store locs that will be invalidated
605
605
  self.temp_vars_by_key = {}
606
606
  self.parsing_context = defaultdict(list)
607
+ self.parsing_context["final_vars"].append(set()) # initialize module-level final scope
607
608
  self.name_info = defaultdict(lambda: {"imported": [], "referenced": [], "assigned": []})
608
609
  self.star_import = False
609
610
  self.kept_lines = []
@@ -634,6 +635,7 @@ class Compiler(Grammar, pickleable_obj):
634
635
  skips, self.skips = self.skips, []
635
636
  docstring, self.docstring = self.docstring, ""
636
637
  parsing_context, self.parsing_context = self.parsing_context, defaultdict(list)
638
+ self.parsing_context["final_vars"].append(set()) # initialize module-level final scope
637
639
  kept_lines, self.kept_lines = self.kept_lines, []
638
640
  num_lines, self.num_lines = self.num_lines, 0
639
641
  remaining_original, self.remaining_original = self.remaining_original, None
@@ -816,12 +818,22 @@ class Compiler(Grammar, pickleable_obj):
816
818
 
817
819
  # name handlers
818
820
  cls.refname <<= attach(cls.name_ref, cls.method("name_handle"))
819
- cls.setname <<= attach(cls.name_ref, cls.method("name_handle", assign=True))
820
- cls.classname <<= attach(
821
+ cls.nonfinal_setname <<= attach(cls.name_ref, cls.method("name_handle", assign=True))
822
+ cls.final_setname <<= attach(
823
+ cls.final_setname_ref,
824
+ cls.method("name_handle", assign=True, is_final=True),
825
+ greedy=True,
826
+ )
827
+ cls.nonfinal_classname <<= attach(
821
828
  cls.name_ref,
822
829
  cls.method("name_handle", assign=True, classname=True),
823
830
  greedy=True,
824
831
  )
832
+ cls.final_classname <<= attach(
833
+ cls.final_setname_ref,
834
+ cls.method("name_handle", assign=True, classname=True, is_final=True),
835
+ greedy=True,
836
+ )
825
837
  cls.expr_setname <<= attach(
826
838
  cls.name_ref,
827
839
  cls.method("name_handle", assign=True, expr_setname=True),
@@ -1055,7 +1067,7 @@ class Compiler(Grammar, pickleable_obj):
1055
1067
  msg + " (add '# NOQA' to suppress)",
1056
1068
  original,
1057
1069
  loc,
1058
- **kwargs,
1070
+ **kwargs # no comma
1059
1071
  )
1060
1072
 
1061
1073
  @contextmanager
@@ -5015,7 +5027,7 @@ class {protocol_var}({tokens}, _coconut.typing.Protocol): pass
5015
5027
 
5016
5028
  @contextmanager
5017
5029
  def add_to_parsing_context(self, name, obj, callbacks_key=None):
5018
- """Pur the given object on the parsing context stack for the given name."""
5030
+ """Put the given object on the parsing context stack for the given name."""
5019
5031
  self.parsing_context[name].append(obj)
5020
5032
  try:
5021
5033
  yield
@@ -5244,7 +5256,8 @@ class {protocol_var}({tokens}, _coconut.typing.Protocol): pass
5244
5256
  try:
5245
5257
  # handles support for class type variables
5246
5258
  with self.type_alias_stmt_manage():
5247
- yield
5259
+ with self.add_to_parsing_context("final_vars", set()):
5260
+ yield
5248
5261
  finally:
5249
5262
  cls_stack.pop()
5250
5263
 
@@ -5257,7 +5270,8 @@ class {protocol_var}({tokens}, _coconut.typing.Protocol): pass
5257
5270
  try:
5258
5271
  # handles support for function type variables
5259
5272
  with self.type_alias_stmt_manage():
5260
- yield
5273
+ with self.add_to_parsing_context("final_vars", set()):
5274
+ yield
5261
5275
  finally:
5262
5276
  if cls_context is not None:
5263
5277
  cls_context["in_method"] = in_method
@@ -5283,11 +5297,15 @@ class {protocol_var}({tokens}, _coconut.typing.Protocol): pass
5283
5297
  ):
5284
5298
  yield
5285
5299
 
5286
- def name_handle(self, original, loc, tokens, assign=False, classname=False, expr_setname=False):
5300
+ def name_handle(self, original, loc, tokens, assign=False, classname=False, expr_setname=False, is_final=False):
5287
5301
  """Handle the given base name."""
5288
- internal_assert(assign if expr_setname else True, "expr_setname should always imply assign", (expr_setname, assign))
5302
+ if expr_setname:
5303
+ internal_assert(assign, "expr_setname should always imply assign", (expr_setname, assign))
5304
+ if is_final:
5305
+ internal_assert(assign and not expr_setname, "only setnames should ever be final", (assign, is_final))
5289
5306
 
5290
5307
  name, = tokens
5308
+
5291
5309
  if name.startswith("\\"):
5292
5310
  name = name[1:]
5293
5311
  escaped = True
@@ -5297,6 +5315,28 @@ class {protocol_var}({tokens}, _coconut.typing.Protocol): pass
5297
5315
  if self.disable_name_check:
5298
5316
  return name
5299
5317
 
5318
+ # raise_or_wrap_error for all errors here to make sure we don't
5319
+ # raise spurious errors if not using the computation graph
5320
+
5321
+ # final variable checking
5322
+ final_vars = self.current_parsing_context("final_vars")
5323
+ self.internal_assert(final_vars is not None, original, loc, "no final_vars context")
5324
+ if (
5325
+ assign
5326
+ and not expr_setname
5327
+ and not escaped
5328
+ and name in final_vars
5329
+ ):
5330
+ return self.raise_or_wrap_error(
5331
+ CoconutSyntaxError,
5332
+ "cannot reassign final variable '{name}'".format(name=name),
5333
+ original,
5334
+ loc,
5335
+ extra="use explicit '\\{name}' syntax to bypass final checking".format(name=name),
5336
+ )
5337
+ if is_final:
5338
+ final_vars.add(name)
5339
+
5300
5340
  # register non-mid-expression variable assignments inside of where statements for later mangling
5301
5341
  if assign and not expr_setname:
5302
5342
  where_context = self.current_parsing_context("where")
@@ -5315,9 +5355,6 @@ class {protocol_var}({tokens}, _coconut.typing.Protocol): pass
5315
5355
  self.internal_assert(expr_setnames_context is not None, original, loc, "found expr_setname outside of has_expr_setname_manage", tokens)
5316
5356
  expr_setnames_context["new_names"].add(name)
5317
5357
 
5318
- # raise_or_wrap_error for all errors here to make sure we don't
5319
- # raise spurious errors if not using the computation graph
5320
-
5321
5358
  if not escaped:
5322
5359
  typevar_info = self.current_parsing_context("typevars")
5323
5360
  if typevar_info is not None:
@@ -859,10 +859,16 @@ class Grammar(object):
859
859
  base_name = regex_item(base_name_regex)
860
860
 
861
861
  refname = Forward()
862
- setname = Forward()
863
862
  expr_setname = Forward()
864
- classname = Forward()
863
+ final_setname = Forward()
864
+ nonfinal_setname = Forward()
865
+ final_classname = Forward()
866
+ nonfinal_classname = Forward()
867
+
865
868
  name_ref = combine(Optional(backslash) + base_name)
869
+ final_setname_ref = keyword("final").suppress() + name_ref
870
+ setname = final_setname | nonfinal_setname
871
+ classname = final_classname | nonfinal_classname
866
872
  unsafe_name = combine(Optional(backslash.suppress()) + base_name)
867
873
 
868
874
  # use unsafe_name for dotted components since name should only be used for base names
@@ -766,17 +766,6 @@ asyncio_Return = StopIteration
766
766
  ''',
767
767
  indent=1,
768
768
  ),
769
- import_tstr=pycondition(
770
- (3, 14),
771
- if_lt='''
772
- try:
773
- import tstr
774
- except ImportError as tstr_import_err:
775
- tstr = _coconut_missing_module(tstr_import_err)
776
- ''',
777
- if_ge='',
778
- indent=1,
779
- ),
780
769
  class_amap=pycondition(
781
770
  (3, 3),
782
771
  if_lt='''
@@ -27,7 +27,10 @@ class _coconut{object}:{COMMENT.EVERYTHING_HERE_MUST_BE_COPIED_TO_STUB_FILE}
27
27
  import async_generator
28
28
  except ImportError as async_generator_import_err:
29
29
  async_generator = _coconut_missing_module(async_generator_import_err)
30
- {import_tstr}
30
+ try:
31
+ import tstr
32
+ except ImportError as tstr_import_err:
33
+ tstr = _coconut_missing_module(tstr_import_err)
31
34
  {import_pickle}
32
35
  {import_OrderedDict}
33
36
  {import_collections_abc}
@@ -106,7 +106,7 @@ from coconut.constants import (
106
106
  pseudo_targets,
107
107
  reserved_vars,
108
108
  packrat_cache_size,
109
- temp_grammar_item_ref_count,
109
+ min_observed_ref_count,
110
110
  indchars,
111
111
  comment_chars,
112
112
  non_syntactic_newline,
@@ -986,7 +986,13 @@ assert _value_useful == -1, "value must end with usefullness obj"
986
986
 
987
987
  def maybe_copy_elem(item, name):
988
988
  """Copy the given grammar element if it's referenced somewhere else."""
989
- item_ref_count = sys.getrefcount(item) if CPYTHON and not on_new_python else float("inf")
989
+ item_ref_count = sys.getrefcount(item) if CPYTHON else float("inf")
990
+ if on_new_python:
991
+ temp_grammar_item_ref_count = 1
992
+ elif isinstance(min_observed_ref_count, dict):
993
+ temp_grammar_item_ref_count = min_observed_ref_count[name]
994
+ else:
995
+ temp_grammar_item_ref_count = min_observed_ref_count
990
996
  internal_assert(lambda: item_ref_count >= temp_grammar_item_ref_count, "add_action got item with too low ref count", (item, type(item), item_ref_count))
991
997
  if item_ref_count <= temp_grammar_item_ref_count:
992
998
  if DEVELOP:
@@ -26,6 +26,7 @@ import platform
26
26
  import re
27
27
  import datetime as dt
28
28
  from warnings import warn
29
+ from collections import defaultdict
29
30
 
30
31
  # -----------------------------------------------------------------------------------------------------------------------
31
32
  # UTILITIES:
@@ -179,8 +180,8 @@ embed_on_internal_exc = get_bool_env_var("COCONUT_EMBED_ON_INTERNAL_EXC", False)
179
180
  test_computation_graph_pickling = False
180
181
 
181
182
  # should be the minimal ref count observed by maybe_copy_elem
182
- temp_grammar_item_ref_count = (
183
- 1 if PY314
183
+ min_observed_ref_count = (
184
+ defaultdict(lambda: 2, attach=1) if PY314
184
185
  else 4 if PY311
185
186
  else 5
186
187
  )
@@ -441,6 +442,7 @@ reserved_vars = (
441
442
  "case",
442
443
  "cases",
443
444
  "where",
445
+ "final",
444
446
  "addpattern",
445
447
  "then",
446
448
  "operator",
@@ -23,10 +23,10 @@ import sys as _coconut_sys
23
23
  # VERSION:
24
24
  # -----------------------------------------------------------------------------------------------------------------------
25
25
 
26
- VERSION = "3.1.2"
26
+ VERSION = "3.2.0"
27
27
  VERSION_NAME = None
28
28
  # False for release, int >= 1 for develop
29
- DEVELOP = 11
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"
@@ -107,6 +107,12 @@ class int(_coconut_py_int):
107
107
  return _coconut.isinstance(inst, (_coconut_py_int, _coconut_py_long))
108
108
  def __subclasscheck__(cls, subcls):
109
109
  return _coconut.issubclass(subcls, (_coconut_py_int, _coconut_py_long))
110
+ def __eq__(cls, other):
111
+ if other is _coconut_py_int or other is _coconut_py_long:
112
+ return True
113
+ return _coconut.type.__eq__(cls, other)
114
+ def __hash__(cls):
115
+ return _coconut.hash(_coconut_py_int)
110
116
  class bytes(_coconut_py_bytes):
111
117
  __slots__ = ()
112
118
  __doc__ = getattr(_coconut_py_bytes, "__doc__", "<see help(py_bytes)>")
@@ -115,6 +121,12 @@ class bytes(_coconut_py_bytes):
115
121
  return _coconut.isinstance(inst, _coconut_py_bytes)
116
122
  def __subclasscheck__(cls, subcls):
117
123
  return _coconut.issubclass(subcls, _coconut_py_bytes)
124
+ def __eq__(cls, other):
125
+ if other is _coconut_py_bytes:
126
+ return True
127
+ return _coconut.type.__eq__(cls, other)
128
+ def __hash__(cls):
129
+ return _coconut.hash(_coconut_py_bytes)
118
130
  def __new__(self, *args):
119
131
  if not args:
120
132
  return b""
@@ -824,9 +824,11 @@ def run_pyprover(**kwargs):
824
824
  def comp_prelude(args=[], **kwargs):
825
825
  """Compiles evhub/coconut-prelude."""
826
826
  call(["git", "clone", prelude_git])
827
- if MYPY and not WINDOWS:
828
- args.extend(["--target", "3.5", "--mypy"])
829
- kwargs["check_errors"] = False
827
+ # disabled --mypy: was causing test_prelude to take ~5 hours due to
828
+ # extremely slow mypy type-checking on the coconut-prelude package
829
+ # if MYPY and not WINDOWS:
830
+ # args.extend(["--target", "3.5", "--mypy"])
831
+ # kwargs["check_errors"] = False
830
832
  call_coconut([os.path.join(prelude, "setup.coco"), "--force"] + args, **kwargs)
831
833
  call_coconut([os.path.join(prelude, "prelude-source"), os.path.join(prelude, "prelude"), "--force"] + args, **kwargs)
832
834
 
@@ -491,6 +491,8 @@ def primary_test_2() -> bool:
491
491
  assert reduce(function=(+), iterable=range(5), initial=-1) == 9 # type: ignore
492
492
  assert takewhile(predicate=ident, iterable=[1, 2, 1, 0, 1]) |> list == [1, 2, 1] # type: ignore
493
493
  assert dropwhile(predicate=(not), iterable=range(5)) |> list == [1, 2, 3, 4] # type: ignore
494
+ assert type(1) == int
495
+ assert type(b"") == bytes
494
496
  @memoize(typed=True)
495
497
  def typed_memoized_func(x):
496
498
  if x is 1:
@@ -499,12 +501,12 @@ def primary_test_2() -> bool:
499
501
  return (x, typed_memoized_func(1))
500
502
  assert typed_memoized_func(1.0) == (1.0, None)
501
503
  assert typed_memoized_func(1.0)[0] |> type == float
502
- @memoize()
503
- def untyped_memoized_func(x=None):
504
+ @memoize(memoize.RECURSIVE, typed=True) # type: ignore
505
+ def recursive_typed_memoized_func(x=None):
504
506
  if x is None:
505
- return (untyped_memoized_func(1), untyped_memoized_func(1.0))
507
+ return (recursive_typed_memoized_func(1), recursive_typed_memoized_func(1.0))
506
508
  return x
507
- assert untyped_memoized_func() |> map$(type) |> tuple == (int, float)
509
+ assert recursive_typed_memoized_func() |> map$(type) |> tuple == (int, float)
508
510
  assert -1 ?↦ abs == 1
509
511
  assert None ?↦ abs is None
510
512
 
@@ -516,4 +518,78 @@ def primary_test_2() -> bool:
516
518
  # repeat the same thing again now that my_match_err.str has been called
517
519
  assert process_map(ident, [my_match_err]) |> list |> str == str([my_match_err]) # type: ignore
518
520
 
521
+ # final variable tests - valid cases
522
+ # 1. Simple final declaration
523
+ final final_x = 1
524
+ assert final_x == 1
525
+ # 2. Final parameter (no reassignment inside)
526
+ def final_param_func(final param) = param * 2
527
+ assert final_param_func(5) == 10
528
+ # 3. Shadowing in inner scope WITH final (allowed)
529
+ final final_outer1 = 1
530
+ def final_shadow_test1():
531
+ final final_outer1 = 2 # shadows with final
532
+ return final_outer1
533
+ assert final_shadow_test1() == 2
534
+ assert final_outer1 == 1 # outer unchanged
535
+ # 4. Shadowing in inner scope WITHOUT final (also allowed - creates new non-final binding)
536
+ final final_outer2 = 10
537
+ def final_shadow_test2():
538
+ final_outer2 = 20 # shadows without final - OK, new binding
539
+ final_outer2 = 30 # can reassign since it's not final in this scope
540
+ return final_outer2
541
+ assert final_shadow_test2() == 30
542
+ assert final_outer2 == 10 # outer unchanged
543
+ # 5. Function parameter shadows outer final (no final needed)
544
+ final final_param_shadow = 100
545
+ def final_param_test(final_param_shadow): # parameter shadows outer final - OK
546
+ return final_param_shadow * 2
547
+ assert final_param_test(5) == 10
548
+ assert final_param_shadow == 100
549
+ # 6. Referencing final (read access is fine)
550
+ final final_readable = 42
551
+ final_other = final_readable + 1
552
+ assert final_other == 43
553
+ # 7. Multiple final declarations (different names)
554
+ final final_a = 1
555
+ final final_b = 2
556
+ assert final_a + final_b == 3
557
+ # 8. Final in class body (class attribute)
558
+ class FinalClass:
559
+ final class_final = 100
560
+ assert FinalClass.class_final == 100
561
+ # 9. Typed final
562
+ final final_typed_val: int = 999
563
+ assert final_typed_val == 999
564
+ # 10. Final in for loop (new final binding per iteration)
565
+ final_result = []
566
+ for final final_i in [1, 2, 3]:
567
+ final_result.append(final_i)
568
+ assert final_result == [1, 2, 3]
569
+ # 11. Escape hatch - can bypass with backslash
570
+ final final_escape_test = 1
571
+ \final_escape_test = 2 # bypasses final check
572
+ assert final_escape_test == 2
573
+ # 12. Class body assignment doesn't conflict with outer final
574
+ final final_class_ok = 1
575
+ class OkClass:
576
+ final_class_ok = 2 # This is a class attribute, different namespace
577
+ assert OkClass.final_class_ok == 2
578
+ assert final_class_ok == 1
579
+ # 13. Comprehension variables are independent (not checked for final)
580
+ final final_comp_var = 1
581
+ final_result2 = [final_comp_var for final_comp_var in [10, 20]] # expr_setname, not checked
582
+ assert final_result2 == [10, 20]
583
+ assert final_comp_var == 1 # outer unchanged
584
+ # 14. Nested function shadowing
585
+ final final_nested = 1
586
+ def final_outer_fn():
587
+ final_nested = 2 # shadows without final - OK
588
+ def final_inner_fn():
589
+ final_nested = 3 # shadows again - OK
590
+ return final_nested
591
+ return final_inner_fn() + final_nested
592
+ assert final_outer_fn() == 5
593
+ assert final_nested == 1
594
+
519
595
  return True
@@ -10,7 +10,7 @@ from coconut.constants import (
10
10
  NUMPY,
11
11
  PY35,
12
12
  PY36,
13
- PY39,
13
+ PY310,
14
14
  PYPY,
15
15
  ) # type: ignore
16
16
  from coconut._pyparsing import USE_COMPUTATION_GRAPH # type: ignore
@@ -335,6 +335,18 @@ def g(x) = x
335
335
  assert parse("[def x -> (x, y) for y in range(10)]", "lenient").count("def") == 2
336
336
  assert parse("123 # derp", "lenient") == "123 # derp"
337
337
 
338
+ assert_raises(-> parse("final x = 1\nx = 2"), CoconutSyntaxError, err_has="final")
339
+ assert_raises(-> parse("final x = 1\nx += 1"), CoconutSyntaxError, err_has="final")
340
+ assert_raises(-> parse("final x = 1\ndel x"), CoconutSyntaxError, err_has="final")
341
+ assert_raises(-> parse("final x = 1\nfor x in [1]: pass"), CoconutSyntaxError, err_has="final")
342
+ assert_raises(-> parse("final x = 1\nwith foo() as x: pass"), CoconutSyntaxError, err_has="final")
343
+ assert_raises(-> parse("final e = 1\ntry: pass\nexcept E as e: pass"), CoconutSyntaxError, err_has="final")
344
+ assert_raises(-> parse("final os = 1\nimport os"), CoconutSyntaxError, err_has="final")
345
+ assert_raises(-> parse("final path = 1\nfrom os import path"), CoconutSyntaxError, err_has="final")
346
+ assert_raises(-> parse("def f(final x): x = 2"), CoconutSyntaxError, err_has="final")
347
+ assert_raises(-> parse("final x = 1\nmatch y:\n case x: pass"), CoconutSyntaxError, err_has="final")
348
+ assert_raises(-> parse("final x = 1\nfinal x = 2"), CoconutSyntaxError, err_has="final")
349
+
338
350
  return True
339
351
 
340
352
 
@@ -768,7 +780,7 @@ def test_extras() -> bool:
768
780
  if NUMPY and PY36:
769
781
  assert test_pandas() is True # .
770
782
  print(".", end="")
771
- if NUMPY and PY39:
783
+ if NUMPY and PY310:
772
784
  assert test_xarray() is True # ..
773
785
  print(".") # newline bc we print stuff after this
774
786
  assert test_setup_none() is True # ...