unpythonic 0.15.2__tar.gz → 0.15.5__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 (80) hide show
  1. {unpythonic-0.15.2/unpythonic.egg-info → unpythonic-0.15.5}/PKG-INFO +42 -45
  2. {unpythonic-0.15.2 → unpythonic-0.15.5}/README.md +33 -33
  3. unpythonic-0.15.5/pyproject.toml +76 -0
  4. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/__init__.py +2 -2
  5. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/arity.py +6 -6
  6. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/conditions.py +8 -13
  7. unpythonic-0.15.5/unpythonic/dialects/__init__.py +17 -0
  8. unpythonic-0.15.5/unpythonic/dialects/lispython.py +88 -0
  9. unpythonic-0.15.5/unpythonic/dialects/listhell.py +35 -0
  10. unpythonic-0.15.5/unpythonic/dialects/pytkell.py +51 -0
  11. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/excutil.py +11 -18
  12. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/let.py +5 -22
  13. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/mathseq.py +7 -0
  14. unpythonic-0.15.5/unpythonic/syntax/00_stuff/letdo.py +980 -0
  15. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/autocurry.py +5 -0
  16. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/autoref.py +1 -1
  17. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/lambdatools.py +1 -1
  18. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/lazify.py +5 -0
  19. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/letdo.py +73 -60
  20. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/letdoutil.py +82 -44
  21. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/scopeanalyzer.py +40 -4
  22. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/tailtools.py +2 -2
  23. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/util.py +1 -1
  24. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/timeutil.py +2 -0
  25. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/typecheck.py +8 -12
  26. unpythonic-0.15.2/AUTHORS.md +0 -22
  27. unpythonic-0.15.2/PKG-INFO +0 -897
  28. unpythonic-0.15.2/setup.cfg +0 -4
  29. unpythonic-0.15.2/setup.py +0 -104
  30. unpythonic-0.15.2/unpythonic/syntax/astcompat.py +0 -69
  31. unpythonic-0.15.2/unpythonic.egg-info/SOURCES.txt +0 -72
  32. unpythonic-0.15.2/unpythonic.egg-info/dependency_links.txt +0 -1
  33. unpythonic-0.15.2/unpythonic.egg-info/not-zip-safe +0 -1
  34. unpythonic-0.15.2/unpythonic.egg-info/top_level.txt +0 -1
  35. {unpythonic-0.15.2 → unpythonic-0.15.5}/LICENSE.md +0 -0
  36. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/amb.py +0 -0
  37. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/assignonce.py +0 -0
  38. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/collections.py +0 -0
  39. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/dispatch.py +0 -0
  40. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/dynassign.py +0 -0
  41. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/ec.py +0 -0
  42. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/env.py +0 -0
  43. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/fix.py +0 -0
  44. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/fold.py +0 -0
  45. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/fploop.py +0 -0
  46. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/fun.py +0 -0
  47. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/funutil.py +0 -0
  48. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/fup.py +0 -0
  49. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/gmemo.py +0 -0
  50. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/gtco.py +0 -0
  51. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/it.py +0 -0
  52. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/lazyutil.py +0 -0
  53. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/lispylet.py +0 -0
  54. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/llist.py +0 -0
  55. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/misc.py +0 -0
  56. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/__init__.py +0 -0
  57. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/client.py +0 -0
  58. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/common.py +0 -0
  59. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/msg.py +0 -0
  60. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/ptyproxy.py +0 -0
  61. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/server.py +0 -0
  62. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/util.py +0 -0
  63. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/numutil.py +0 -0
  64. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/regutil.py +0 -0
  65. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/seq.py +0 -0
  66. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/singleton.py +0 -0
  67. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/slicing.py +0 -0
  68. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/symbol.py +0 -0
  69. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/__init__.py +0 -0
  70. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/dbg.py +0 -0
  71. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/forall.py +0 -0
  72. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/ifexprs.py +0 -0
  73. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/letsyntax.py +0 -0
  74. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/nameutil.py +0 -0
  75. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/nb.py +0 -0
  76. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/prefix.py +0 -0
  77. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/simplelet.py +0 -0
  78. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/testingtools.py +0 -0
  79. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/tco.py +0 -0
  80. {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/test/fixtures.py +0 -0
@@ -1,13 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unpythonic
3
- Version: 0.15.2
3
+ Version: 0.15.5
4
4
  Summary: Supercharge your Python with parts of Lisp and Haskell.
5
- Home-page: https://github.com/Technologicat/unpythonic
6
- Author: Juha Jeronen
7
- Author-email: juha.m.jeronen@gmail.com
8
- License: BSD
9
5
  Keywords: functional-programming,language-extension,syntactic-macros,tail-call-optimization,tco,continuations,currying,lazy-evaluation,dynamic-variable,macros,lisp,scheme,racket,haskell
10
- Platform: Linux
6
+ Author-Email: Juha Jeronen <juha.m.jeronen@gmail.com>
7
+ License: BSD
11
8
  Classifier: Development Status :: 4 - Beta
12
9
  Classifier: Environment :: Console
13
10
  Classifier: Intended Audience :: Developers
@@ -15,26 +12,26 @@ Classifier: License :: OSI Approved :: BSD License
15
12
  Classifier: Operating System :: POSIX :: Linux
16
13
  Classifier: Programming Language :: Python
17
14
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.6
19
- Classifier: Programming Language :: Python :: 3.7
20
15
  Classifier: Programming Language :: Python :: 3.8
21
16
  Classifier: Programming Language :: Python :: 3.9
22
17
  Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
23
20
  Classifier: Programming Language :: Python :: Implementation :: CPython
24
21
  Classifier: Programming Language :: Python :: Implementation :: PyPy
25
22
  Classifier: Topic :: Software Development :: Libraries
26
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
- Provides: unpythonic
28
- Requires-Python: >=3.6,<3.11
24
+ Project-URL: Repository, https://github.com/Technologicat/unpythonic
25
+ Requires-Python: <3.13,>=3.8
26
+ Requires-Dist: mcpyrate>=3.6.4
27
+ Requires-Dist: sympy>=1.13
29
28
  Description-Content-Type: text/markdown
30
- License-File: LICENSE.md
31
- License-File: AUTHORS.md
32
29
 
33
30
  # Unpythonic: Python meets Lisp and Haskell
34
31
 
35
32
  In the spirit of [toolz](https://github.com/pytoolz/toolz), we provide missing features for Python, mainly from the list processing tradition, but with some Haskellisms mixed in. We extend the language with a set of [syntactic macros](https://en.wikipedia.org/wiki/Macro_(computer_science)#Syntactic_macros). We also provide an in-process, background [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) server for live inspection and hot-patching. The emphasis is on **clear, pythonic syntax**, **making features work together**, and **obsessive correctness**.
36
33
 
37
- ![100% Python](https://img.shields.io/github/languages/top/Technologicat/unpythonic) ![supported language versions](https://img.shields.io/pypi/pyversions/unpythonic) ![supported implementations](https://img.shields.io/pypi/implementation/unpythonic) ![CI status](https://img.shields.io/github/workflow/status/Technologicat/unpythonic/Python%20package) [![codecov](https://codecov.io/gh/Technologicat/unpythonic/branch/master/graph/badge.svg)](https://codecov.io/gh/Technologicat/unpythonic)
34
+ ![100% Python](https://img.shields.io/github/languages/top/Technologicat/unpythonic) ![supported language versions](https://img.shields.io/pypi/pyversions/unpythonic) ![supported implementations](https://img.shields.io/pypi/implementation/unpythonic) ![CI status](https://img.shields.io/github/actions/workflow/status/Technologicat/unpythonic/python-package.yml?branch=master) [![codecov](https://codecov.io/gh/Technologicat/unpythonic/branch/master/graph/badge.svg)](https://codecov.io/gh/Technologicat/unpythonic)
38
35
  ![version on PyPI](https://img.shields.io/pypi/v/unpythonic) ![PyPI package format](https://img.shields.io/pypi/format/unpythonic) ![dependency status](https://img.shields.io/librariesio/github/Technologicat/unpythonic)
39
36
  ![license: BSD](https://img.shields.io/pypi/l/unpythonic) ![open issues](https://img.shields.io/github/issues/Technologicat/unpythonic) [![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen)](http://makeapullrequest.com/)
40
37
 
@@ -47,7 +44,7 @@ None required.
47
44
 
48
45
  - [`mcpyrate`](https://github.com/Technologicat/mcpyrate) optional, to enable the syntactic macro layer, an interactive macro REPL, and some example dialects.
49
46
 
50
- The 0.15.x series should run on CPython 3.6, 3.7, 3.8, 3.9 and 3.10, and PyPy3 (language versions 3.6, 3.7 and 3.8); the [CI](https://en.wikipedia.org/wiki/Continuous_integration) process verifies the tests pass on those platforms. [Long-term support roadmap](https://github.com/Technologicat/unpythonic/issues/1).
47
+ As of v0.15.3, `unpythonic` runs on CPython 3.8, 3.9 and 3.10, 3.11, 3.12, and PyPy3 (language versions 3.8, 3.9, 3.10); the [CI](https://en.wikipedia.org/wiki/Continuous_integration) process verifies the tests pass on those platforms. New Python versions are added and old ones are removed following the [Long-term support roadmap](https://github.com/Technologicat/unpythonic/issues/1).
51
48
 
52
49
 
53
50
  ### Documentation
@@ -626,13 +623,13 @@ As usual in test frameworks, the testing constructs behave somewhat like `assert
626
623
  ```python
627
624
  from unpythonic.syntax import macros, let, letseq, letrec
628
625
 
629
- x = let[[a << 1, b << 2] in a + b]
630
- y = letseq[[c << 1, # LET SEQuential, like Scheme's let*
631
- c << 2 * c,
632
- c << 2 * c] in
626
+ x = let[[a := 1, b := 2] in a + b]
627
+ y = letseq[[c := 1, # LET SEQuential, like Scheme's let*
628
+ c := 2 * c,
629
+ c := 2 * c] in
633
630
  c]
634
- z = letrec[[evenp << (lambda x: (x == 0) or oddp(x - 1)), # LET mutually RECursive, like in Scheme
635
- oddp << (lambda x: (x != 0) and evenp(x - 1))]
631
+ z = letrec[[evenp := (lambda x: (x == 0) or oddp(x - 1)), # LET mutually RECursive, like in Scheme
632
+ oddp := (lambda x: (x != 0) and evenp(x - 1))]
636
633
  in evenp(42)]
637
634
  ```
638
635
  </details>
@@ -643,10 +640,10 @@ z = letrec[[evenp << (lambda x: (x == 0) or oddp(x - 1)), # LET mutually RECurs
643
640
  ```python
644
641
  from unpythonic.syntax import macros, dlet
645
642
 
646
- # Up to Python 3.8, use `@dlet(x << 0)` instead
647
- @dlet[x << 0] # let-over-lambda for Python
643
+ # In Python 3.8, use `@dlet(x << 0)` instead; in Python 3.9, use `@dlet(x := 0)`
644
+ @dlet[x := 0] # let-over-lambda for Python
648
645
  def count():
649
- return x << x + 1 # `name << value` rebinds in the let env
646
+ return x := x + 1 # `name := value` rebinds in the let env
650
647
  assert count() == 1
651
648
  assert count() == 2
652
649
  ```
@@ -658,8 +655,8 @@ assert count() == 2
658
655
  ```python
659
656
  from unpythonic.syntax import macros, do, local, delete
660
657
 
661
- x = do[local[a << 21],
662
- local[b << 2 * a],
658
+ x = do[local[a := 21],
659
+ local[b := 2 * a],
663
660
  print(b),
664
661
  delete[b], # do[] local variables can be deleted, too
665
662
  4 * a]
@@ -783,8 +780,8 @@ assert square.__name__ == "square"
783
780
  # - brackets denote a multiple-expression lambda body
784
781
  # (if you want to have one expression that is a literal list,
785
782
  # double the brackets: `lambda x: [[5 * x]]`)
786
- # - local[name << value] makes an expression-local variable
787
- g = lambda x: [local[y << 2 * x],
783
+ # - local[name := value] makes an expression-local variable
784
+ g = lambda x: [local[y := 2 * x],
788
785
  y + 1]
789
786
  assert g(10) == 21
790
787
  ```
@@ -836,35 +833,35 @@ assert (my_map, double, (q, 1, 2, 3)) == (ll, 2, 4, 6)
836
833
  ```
837
834
  </details>
838
835
 
839
- ## Installation
840
-
841
- **PyPI**
836
+ ## Install & uninstall
842
837
 
843
- ``pip3 install unpythonic --user``
838
+ ### From PyPI
844
839
 
845
- or
846
-
847
- ``sudo pip3 install unpythonic``
848
-
849
- **GitHub**
840
+ ```bash
841
+ pip install unpythonic
842
+ ```
850
843
 
851
- Clone (or pull) from GitHub. Then,
844
+ ### From source
852
845
 
853
- ``python3 setup.py install --user``
846
+ Clone the repo from GitHub. Then, navigate to it in a terminal, and:
854
847
 
855
- or
848
+ ```bash
849
+ pip install . --no-compile
850
+ ```
856
851
 
857
- ``sudo python3 setup.py install``
852
+ If you intend to use the macro layer of `unpythonic`, the `--no-compile` flag is important. It prevents an **incorrect** precompilation, without macro support, that `pip install` would otherwise do at its `bdist_wheel` step.
858
853
 
859
- **Uninstall**
854
+ For most Python projects such precompilation is just fine - it's just macro-enabled projects that shouldn't be precompiled with standard tools.
860
855
 
861
- Uninstallation must be invoked in a folder which has no subfolder called ``unpythonic``, so that ``pip`` recognizes it as a package name (instead of a filename). Then,
856
+ If `--no-compile` is NOT used, the precompiled bytecode cache may cause errors such as `ImportError: cannot import name 'macros' from 'mcpyrate.quotes'`, when you try to e.g. `from unpythonic.syntax import macros, let`. In-tree, it might work, but against an installed copy, it will fail. It has happened that my CI setup did not detect this kind of failure.
862
857
 
863
- ``pip3 uninstall unpythonic``
858
+ This is a common issue when using macro expanders in Python.
864
859
 
865
- or
860
+ ### Uninstall
866
861
 
867
- ``sudo pip3 uninstall unpythonic``
862
+ ```bash
863
+ pip uninstall unpythonic
864
+ ```
868
865
 
869
866
 
870
867
  ## Support
@@ -2,7 +2,7 @@
2
2
 
3
3
  In the spirit of [toolz](https://github.com/pytoolz/toolz), we provide missing features for Python, mainly from the list processing tradition, but with some Haskellisms mixed in. We extend the language with a set of [syntactic macros](https://en.wikipedia.org/wiki/Macro_(computer_science)#Syntactic_macros). We also provide an in-process, background [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) server for live inspection and hot-patching. The emphasis is on **clear, pythonic syntax**, **making features work together**, and **obsessive correctness**.
4
4
 
5
- ![100% Python](https://img.shields.io/github/languages/top/Technologicat/unpythonic) ![supported language versions](https://img.shields.io/pypi/pyversions/unpythonic) ![supported implementations](https://img.shields.io/pypi/implementation/unpythonic) ![CI status](https://img.shields.io/github/workflow/status/Technologicat/unpythonic/Python%20package) [![codecov](https://codecov.io/gh/Technologicat/unpythonic/branch/master/graph/badge.svg)](https://codecov.io/gh/Technologicat/unpythonic)
5
+ ![100% Python](https://img.shields.io/github/languages/top/Technologicat/unpythonic) ![supported language versions](https://img.shields.io/pypi/pyversions/unpythonic) ![supported implementations](https://img.shields.io/pypi/implementation/unpythonic) ![CI status](https://img.shields.io/github/actions/workflow/status/Technologicat/unpythonic/python-package.yml?branch=master) [![codecov](https://codecov.io/gh/Technologicat/unpythonic/branch/master/graph/badge.svg)](https://codecov.io/gh/Technologicat/unpythonic)
6
6
  ![version on PyPI](https://img.shields.io/pypi/v/unpythonic) ![PyPI package format](https://img.shields.io/pypi/format/unpythonic) ![dependency status](https://img.shields.io/librariesio/github/Technologicat/unpythonic)
7
7
  ![license: BSD](https://img.shields.io/pypi/l/unpythonic) ![open issues](https://img.shields.io/github/issues/Technologicat/unpythonic) [![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen)](http://makeapullrequest.com/)
8
8
 
@@ -15,7 +15,7 @@ None required.
15
15
 
16
16
  - [`mcpyrate`](https://github.com/Technologicat/mcpyrate) optional, to enable the syntactic macro layer, an interactive macro REPL, and some example dialects.
17
17
 
18
- The 0.15.x series should run on CPython 3.6, 3.7, 3.8, 3.9 and 3.10, and PyPy3 (language versions 3.6, 3.7 and 3.8); the [CI](https://en.wikipedia.org/wiki/Continuous_integration) process verifies the tests pass on those platforms. [Long-term support roadmap](https://github.com/Technologicat/unpythonic/issues/1).
18
+ As of v0.15.3, `unpythonic` runs on CPython 3.8, 3.9 and 3.10, 3.11, 3.12, and PyPy3 (language versions 3.8, 3.9, 3.10); the [CI](https://en.wikipedia.org/wiki/Continuous_integration) process verifies the tests pass on those platforms. New Python versions are added and old ones are removed following the [Long-term support roadmap](https://github.com/Technologicat/unpythonic/issues/1).
19
19
 
20
20
 
21
21
  ### Documentation
@@ -594,13 +594,13 @@ As usual in test frameworks, the testing constructs behave somewhat like `assert
594
594
  ```python
595
595
  from unpythonic.syntax import macros, let, letseq, letrec
596
596
 
597
- x = let[[a << 1, b << 2] in a + b]
598
- y = letseq[[c << 1, # LET SEQuential, like Scheme's let*
599
- c << 2 * c,
600
- c << 2 * c] in
597
+ x = let[[a := 1, b := 2] in a + b]
598
+ y = letseq[[c := 1, # LET SEQuential, like Scheme's let*
599
+ c := 2 * c,
600
+ c := 2 * c] in
601
601
  c]
602
- z = letrec[[evenp << (lambda x: (x == 0) or oddp(x - 1)), # LET mutually RECursive, like in Scheme
603
- oddp << (lambda x: (x != 0) and evenp(x - 1))]
602
+ z = letrec[[evenp := (lambda x: (x == 0) or oddp(x - 1)), # LET mutually RECursive, like in Scheme
603
+ oddp := (lambda x: (x != 0) and evenp(x - 1))]
604
604
  in evenp(42)]
605
605
  ```
606
606
  </details>
@@ -611,10 +611,10 @@ z = letrec[[evenp << (lambda x: (x == 0) or oddp(x - 1)), # LET mutually RECurs
611
611
  ```python
612
612
  from unpythonic.syntax import macros, dlet
613
613
 
614
- # Up to Python 3.8, use `@dlet(x << 0)` instead
615
- @dlet[x << 0] # let-over-lambda for Python
614
+ # In Python 3.8, use `@dlet(x << 0)` instead; in Python 3.9, use `@dlet(x := 0)`
615
+ @dlet[x := 0] # let-over-lambda for Python
616
616
  def count():
617
- return x << x + 1 # `name << value` rebinds in the let env
617
+ return x := x + 1 # `name := value` rebinds in the let env
618
618
  assert count() == 1
619
619
  assert count() == 2
620
620
  ```
@@ -626,8 +626,8 @@ assert count() == 2
626
626
  ```python
627
627
  from unpythonic.syntax import macros, do, local, delete
628
628
 
629
- x = do[local[a << 21],
630
- local[b << 2 * a],
629
+ x = do[local[a := 21],
630
+ local[b := 2 * a],
631
631
  print(b),
632
632
  delete[b], # do[] local variables can be deleted, too
633
633
  4 * a]
@@ -751,8 +751,8 @@ assert square.__name__ == "square"
751
751
  # - brackets denote a multiple-expression lambda body
752
752
  # (if you want to have one expression that is a literal list,
753
753
  # double the brackets: `lambda x: [[5 * x]]`)
754
- # - local[name << value] makes an expression-local variable
755
- g = lambda x: [local[y << 2 * x],
754
+ # - local[name := value] makes an expression-local variable
755
+ g = lambda x: [local[y := 2 * x],
756
756
  y + 1]
757
757
  assert g(10) == 21
758
758
  ```
@@ -804,35 +804,35 @@ assert (my_map, double, (q, 1, 2, 3)) == (ll, 2, 4, 6)
804
804
  ```
805
805
  </details>
806
806
 
807
- ## Installation
807
+ ## Install & uninstall
808
808
 
809
- **PyPI**
809
+ ### From PyPI
810
810
 
811
- ``pip3 install unpythonic --user``
812
-
813
- or
814
-
815
- ``sudo pip3 install unpythonic``
816
-
817
- **GitHub**
811
+ ```bash
812
+ pip install unpythonic
813
+ ```
818
814
 
819
- Clone (or pull) from GitHub. Then,
815
+ ### From source
820
816
 
821
- ``python3 setup.py install --user``
817
+ Clone the repo from GitHub. Then, navigate to it in a terminal, and:
822
818
 
823
- or
819
+ ```bash
820
+ pip install . --no-compile
821
+ ```
824
822
 
825
- ``sudo python3 setup.py install``
823
+ If you intend to use the macro layer of `unpythonic`, the `--no-compile` flag is important. It prevents an **incorrect** precompilation, without macro support, that `pip install` would otherwise do at its `bdist_wheel` step.
826
824
 
827
- **Uninstall**
825
+ For most Python projects such precompilation is just fine - it's just macro-enabled projects that shouldn't be precompiled with standard tools.
828
826
 
829
- Uninstallation must be invoked in a folder which has no subfolder called ``unpythonic``, so that ``pip`` recognizes it as a package name (instead of a filename). Then,
827
+ If `--no-compile` is NOT used, the precompiled bytecode cache may cause errors such as `ImportError: cannot import name 'macros' from 'mcpyrate.quotes'`, when you try to e.g. `from unpythonic.syntax import macros, let`. In-tree, it might work, but against an installed copy, it will fail. It has happened that my CI setup did not detect this kind of failure.
830
828
 
831
- ``pip3 uninstall unpythonic``
829
+ This is a common issue when using macro expanders in Python.
832
830
 
833
- or
831
+ ### Uninstall
834
832
 
835
- ``sudo pip3 uninstall unpythonic``
833
+ ```bash
834
+ pip uninstall unpythonic
835
+ ```
836
836
 
837
837
 
838
838
  ## Support
@@ -0,0 +1,76 @@
1
+ [project]
2
+ name = "unpythonic"
3
+ description = "Supercharge your Python with parts of Lisp and Haskell."
4
+ authors = [
5
+ { name = "Juha Jeronen", email = "juha.m.jeronen@gmail.com" },
6
+ ]
7
+ requires-python = ">=3.8,<3.13"
8
+ readme = "README.md"
9
+ dynamic = []
10
+ dependencies = [
11
+ "mcpyrate>=3.6.4",
12
+ "sympy>=1.13",
13
+ ]
14
+ keywords = [
15
+ "functional-programming",
16
+ "language-extension",
17
+ "syntactic-macros",
18
+ "tail-call-optimization",
19
+ "tco",
20
+ "continuations",
21
+ "currying",
22
+ "lazy-evaluation",
23
+ "dynamic-variable",
24
+ "macros",
25
+ "lisp",
26
+ "scheme",
27
+ "racket",
28
+ "haskell",
29
+ ]
30
+ classifiers = [
31
+ "Development Status :: 4 - Beta",
32
+ "Environment :: Console",
33
+ "Intended Audience :: Developers",
34
+ "License :: OSI Approved :: BSD License",
35
+ "Operating System :: POSIX :: Linux",
36
+ "Programming Language :: Python",
37
+ "Programming Language :: Python :: 3",
38
+ "Programming Language :: Python :: 3.8",
39
+ "Programming Language :: Python :: 3.9",
40
+ "Programming Language :: Python :: 3.10",
41
+ "Programming Language :: Python :: 3.11",
42
+ "Programming Language :: Python :: 3.12",
43
+ "Programming Language :: Python :: Implementation :: CPython",
44
+ "Programming Language :: Python :: Implementation :: PyPy",
45
+ "Topic :: Software Development :: Libraries",
46
+ "Topic :: Software Development :: Libraries :: Python Modules",
47
+ ]
48
+ version = "0.15.5"
49
+
50
+ [project.license]
51
+ text = "BSD"
52
+
53
+ [project.urls]
54
+ Repository = "https://github.com/Technologicat/unpythonic"
55
+
56
+ [build-system]
57
+ requires = [
58
+ "pdm-backend",
59
+ ]
60
+ build-backend = "pdm.backend"
61
+
62
+ [tool.pdm.version]
63
+ source = "file"
64
+ path = "unpythonic/__init__.py"
65
+
66
+ [tool.pdm.build]
67
+ includes = [
68
+ "unpythonic",
69
+ ]
70
+ excludes = [
71
+ "**/tests",
72
+ "**/__pycache__",
73
+ ]
74
+
75
+ [tool.mypy]
76
+ show_error_codes = true
@@ -7,7 +7,7 @@ If you have ``mcpyrate`` installed, see also ``unpythonic.syntax``
7
7
  for a trip down the rabbit hole.
8
8
  """
9
9
 
10
- __version__ = '0.15.2'
10
+ __version__ = '0.15.5'
11
11
 
12
12
  from .amb import * # noqa: F401, F403
13
13
  from .arity import * # noqa: F401, F403
@@ -26,7 +26,7 @@ from .fup import * # noqa: F401, F403
26
26
  from .gmemo import * # noqa: F401, F403
27
27
  from .gtco import * # noqa: F401, F403
28
28
  from .it import * # noqa: F401, F403
29
- from .let import * # no guarantees on evaluation order (before Python 3.6), nice syntax # noqa: F401, F403
29
+ from .let import * # # noqa: F401, F403
30
30
 
31
31
  # As of 0.15.0, lispylet is nowadays primarily a code generation target API for macros.
32
32
  from .lispylet import (let as ordered_let, letrec as ordered_letrec, # noqa: F401
@@ -21,7 +21,7 @@ class UnknownArity(ValueError):
21
21
  """Raised when the arity of a function cannot be inspected."""
22
22
 
23
23
  # HACK: some built-ins report incorrect arities (0, 0) at least in Python 3.4
24
- # TODO: re-test on 3.8 and on PyPy3 (3.7), just to be sure.
24
+ # TODO: re-test on 3.8, 3.9, 3.10, 3.11, 3.12 and on PyPy3 (3.8 and later), just to be sure.
25
25
  #
26
26
  # Full list of built-ins:
27
27
  # https://docs.python.org/3/library/functions.html
@@ -208,7 +208,7 @@ def arities(f):
208
208
  This uses inspect.signature; note that the signature of builtin functions
209
209
  cannot be inspected. This is worked around to some extent, but e.g.
210
210
  methods of built-in classes (such as ``list``) might not be inspectable
211
- (at least on CPython < 3.7).
211
+ (at least on old CPython < 3.7).
212
212
 
213
213
  For bound methods, ``self`` or ``cls`` does not count toward the arity,
214
214
  because these are passed implicitly by Python. Note a `@classmethod` becomes
@@ -352,10 +352,10 @@ def resolve_bindings(f, *args, **kwargs):
352
352
  This is an inspection tool, which does not actually call `f`. This is useful for memoizers
353
353
  and other similar decorators that need a canonical representation of `f`'s parameter bindings.
354
354
 
355
- **NOTE**: As of v0.15.0, this is a thin wrapper on top of `inspect.Signature.bind`,
356
- which was added in Python 3.5. In `unpythonic` 0.14.2 and 0.14.3, we used to have
357
- our own implementation of the parameter binding algorithm (that ran also on Python 3.4),
358
- but it is no longer needed, since now we support only Python 3.6 and later.
355
+ **NOTE**: This is a thin wrapper on top of `inspect.Signature.bind`, which was added in Python 3.5.
356
+ In `unpythonic` 0.14.2 and 0.14.3, we used to have our own implementation of the parameter binding
357
+ algorithm (that ran also on Python 3.4), but it is no longer needed, since as of v0.15.3,
358
+ we support only Python 3.8 and later.
359
359
 
360
360
  The only thing we do beside call `inspect.Signature.bind` is that we apply default values
361
361
  (from the definition of `f`) automatically.
@@ -123,10 +123,10 @@ def signal(condition, *, cause=None, protocol=None):
123
123
  The return value is the input `condition`, canonized to an instance
124
124
  (even if originally, an exception *type* was passed to `signal`),
125
125
  with its `__cause__` and `__protocol__` attributes filled in,
126
- and with a traceback attached (on Python 3.7+). For example, the
127
- `error` protocol uses the return value to chain the unhandled signal
128
- properly into a `ControlError` exception; as a result, the error report
129
- looks like a standard exception chain, with nice-looking tracebacks.
126
+ and with a traceback attached. For example, the `error` protocol
127
+ uses the return value to chain the unhandled signal properly into
128
+ a `ControlError` exception; as a result, the error report looks
129
+ like a standard exception chain, with nice-looking tracebacks.
130
130
 
131
131
  If you want to error out on unhandled conditions, see `error`, which is
132
132
  otherwise the same as `signal`, except it raises if `signal` would have
@@ -162,9 +162,8 @@ def signal(condition, *, cause=None, protocol=None):
162
162
  You can signal any exception or warning object, both builtins and any
163
163
  custom ones.
164
164
 
165
- On Python 3.7 and later, the exception object representing the signaled
166
- condition is equipped with a traceback, just like a raised exception.
167
- On Python 3.6 this is not possible, so the traceback is `None`.
165
+ The exception object representing the signaled condition is equipped
166
+ with a traceback, just like a raised exception.
168
167
  """
169
168
  # Since the handler is called normally, we don't unwind the call stack,
170
169
  # remaining inside the `signal()` call in the low-level code.
@@ -225,12 +224,8 @@ def _prepare_signal_instance(condition, *, cause, protocol, stacklevel):
225
224
  condition.__cause__ = cause
226
225
  condition.__protocol__ = protocol
227
226
 
228
- # Embed a stack trace in the signal, like Python does for raised exceptions.
229
- # This only works on Python 3.7 and later, because we need to create a traceback object in pure Python code.
230
- try:
231
- condition = equip_with_traceback(condition, stacklevel=stacklevel)
232
- except NotImplementedError: # pragma: no cover
233
- pass # well, we tried!
227
+ # Embed a stack trace in the signal, like Python does for raised exceptions. This API was added in Python 3.7.
228
+ condition = equip_with_traceback(condition, stacklevel=stacklevel)
234
229
 
235
230
  return condition
236
231
 
@@ -0,0 +1,17 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Dialects: Python the way you want it.
3
+
4
+ These dialects, i.e. whole-module syntax transformations, are powered by
5
+ `mcpyrate`'s dialect subsystem. The user manual is at:
6
+ https://github.com/Technologicat/mcpyrate/blob/master/doc/dialects.md
7
+
8
+ We provide these dialects mainly to demonstrate how to use that subsystem
9
+ to customize Python beyond what a local macro expander can do.
10
+
11
+ For examples of how to use these particular dialects, see the unit tests.
12
+ """
13
+
14
+ # re-exports
15
+ from .lispython import * # noqa: F401, F403
16
+ from .listhell import * # noqa: F401, F403
17
+ from .pytkell import * # noqa: F401, F403
@@ -0,0 +1,88 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Lispython: The love child of Python and Scheme.
3
+
4
+ Powered by `mcpyrate` and `unpythonic`.
5
+ """
6
+
7
+ __all__ = ["Lispython", "Lispy"]
8
+
9
+ __version__ = '2.0.0'
10
+
11
+ from mcpyrate.quotes import macros, q # noqa: F401
12
+
13
+ from mcpyrate.dialects import Dialect
14
+ from mcpyrate.splicing import splice_dialect
15
+
16
+ class Lispython(Dialect):
17
+ """**Schemers rejoice!**
18
+
19
+ Multiple musings mix in a lambda,
20
+ Lament no longer the lack of let.
21
+ Languish no longer labelless, lambda,
22
+ Linked lists cons and fold.
23
+ Tail-call into recursion divine,
24
+ The final value always provide.
25
+ """
26
+
27
+ def transform_ast(self, tree): # tree is an ast.Module
28
+ with q as template:
29
+ __lang__ = "Lispython" # noqa: F841, just provide it to user code.
30
+ from unpythonic.syntax import (macros, tco, autoreturn, # noqa: F401, F811
31
+ multilambda, quicklambda, namedlambda, fn,
32
+ where,
33
+ let, letseq, letrec,
34
+ dlet, dletseq, dletrec,
35
+ blet, bletseq, bletrec,
36
+ local, delete, do, do0,
37
+ let_syntax, abbrev, block, expr,
38
+ cond)
39
+ from unpythonic import cons, car, cdr, ll, llist, nil, prod, dyn, Values # noqa: F401, F811
40
+ with autoreturn, quicklambda, multilambda, namedlambda, tco:
41
+ __paste_here__ # noqa: F821, just a splicing marker.
42
+
43
+ # Beginning with 3.6.0, `mcpyrate` makes available the source location info
44
+ # of the dialect-import that imported this dialect.
45
+ if hasattr(self, "lineno"): # mcpyrate 3.6.0+
46
+ tree.body = splice_dialect(tree.body, template, "__paste_here__",
47
+ lineno=self.lineno, col_offset=self.col_offset)
48
+ else:
49
+ tree.body = splice_dialect(tree.body, template, "__paste_here__")
50
+
51
+ return tree
52
+
53
+
54
+ class Lispy(Dialect):
55
+ """**Pythonistas rejoice!**
56
+
57
+ O language like Lisp, like Python!
58
+ Semantic changes sensibly carry,
59
+ Python's primary virtue vindicate.
60
+ Ire me not with implicit imports,
61
+ Let my IDE label mistakes.
62
+ """
63
+
64
+ def transform_ast(self, tree): # tree is an ast.Module
65
+ with q as template:
66
+ __lang__ = "Lispy" # noqa: F841, just provide it to user code.
67
+ from unpythonic.syntax import (macros, tco, autoreturn, # noqa: F401, F811
68
+ multilambda, quicklambda, namedlambda)
69
+ # The important point is none of these expect the user code to look like
70
+ # anything but regular Python, so IDEs won't yell about undefined names;
71
+ # just the semantics are slightly different.
72
+ #
73
+ # Even if the user code uses `fn[]` (to make `quicklambda` actually do anything),
74
+ # that macro must be explicitly imported. It works, because `splice_dialect`
75
+ # hoists macro-imports from the top level of the user code into the top level
76
+ # of the template.
77
+ with autoreturn, quicklambda, multilambda, namedlambda, tco:
78
+ __paste_here__ # noqa: F821, just a splicing marker.
79
+
80
+ # Beginning with 3.6.0, `mcpyrate` makes available the source location info
81
+ # of the dialect-import that imported this dialect.
82
+ if hasattr(self, "lineno"): # mcpyrate 3.6.0+
83
+ tree.body = splice_dialect(tree.body, template, "__paste_here__",
84
+ lineno=self.lineno, col_offset=self.col_offset)
85
+ else:
86
+ tree.body = splice_dialect(tree.body, template, "__paste_here__")
87
+
88
+ return tree
@@ -0,0 +1,35 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Listhell: It's not Lisp, it's not Python, it's not Haskell.
3
+
4
+ Powered by `mcpyrate` and `unpythonic`.
5
+ """
6
+
7
+ __all__ = ["Listhell"]
8
+
9
+ __version__ = '2.0.0'
10
+
11
+ from mcpyrate.quotes import macros, q # noqa: F401
12
+
13
+ from mcpyrate.dialects import Dialect
14
+ from mcpyrate.splicing import splice_dialect
15
+
16
+ class Listhell(Dialect):
17
+ def transform_ast(self, tree): # tree is an ast.Module
18
+ with q as template:
19
+ __lang__ = "Listhell" # noqa: F841, just provide it to user code.
20
+ from unpythonic.syntax import macros, prefix, q, u, kw, autocurry # noqa: F401, F811
21
+ # Auxiliary syntax elements for the macros
22
+ from unpythonic import apply # noqa: F401
23
+ from unpythonic import composerc as compose # compose from Right, Currying # noqa: F401
24
+ with prefix, autocurry:
25
+ __paste_here__ # noqa: F821, just a splicing marker.
26
+
27
+ # Beginning with 3.6.0, `mcpyrate` makes available the source location info
28
+ # of the dialect-import that imported this dialect.
29
+ if hasattr(self, "lineno"): # mcpyrate 3.6.0+
30
+ tree.body = splice_dialect(tree.body, template, "__paste_here__",
31
+ lineno=self.lineno, col_offset=self.col_offset)
32
+ else:
33
+ tree.body = splice_dialect(tree.body, template, "__paste_here__")
34
+
35
+ return tree