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.
- {unpythonic-0.15.2/unpythonic.egg-info → unpythonic-0.15.5}/PKG-INFO +42 -45
- {unpythonic-0.15.2 → unpythonic-0.15.5}/README.md +33 -33
- unpythonic-0.15.5/pyproject.toml +76 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/__init__.py +2 -2
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/arity.py +6 -6
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/conditions.py +8 -13
- unpythonic-0.15.5/unpythonic/dialects/__init__.py +17 -0
- unpythonic-0.15.5/unpythonic/dialects/lispython.py +88 -0
- unpythonic-0.15.5/unpythonic/dialects/listhell.py +35 -0
- unpythonic-0.15.5/unpythonic/dialects/pytkell.py +51 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/excutil.py +11 -18
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/let.py +5 -22
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/mathseq.py +7 -0
- unpythonic-0.15.5/unpythonic/syntax/00_stuff/letdo.py +980 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/autocurry.py +5 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/autoref.py +1 -1
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/lambdatools.py +1 -1
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/lazify.py +5 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/letdo.py +73 -60
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/letdoutil.py +82 -44
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/scopeanalyzer.py +40 -4
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/tailtools.py +2 -2
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/util.py +1 -1
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/timeutil.py +2 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/typecheck.py +8 -12
- unpythonic-0.15.2/AUTHORS.md +0 -22
- unpythonic-0.15.2/PKG-INFO +0 -897
- unpythonic-0.15.2/setup.cfg +0 -4
- unpythonic-0.15.2/setup.py +0 -104
- unpythonic-0.15.2/unpythonic/syntax/astcompat.py +0 -69
- unpythonic-0.15.2/unpythonic.egg-info/SOURCES.txt +0 -72
- unpythonic-0.15.2/unpythonic.egg-info/dependency_links.txt +0 -1
- unpythonic-0.15.2/unpythonic.egg-info/not-zip-safe +0 -1
- unpythonic-0.15.2/unpythonic.egg-info/top_level.txt +0 -1
- {unpythonic-0.15.2 → unpythonic-0.15.5}/LICENSE.md +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/amb.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/assignonce.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/collections.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/dispatch.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/dynassign.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/ec.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/env.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/fix.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/fold.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/fploop.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/fun.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/funutil.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/fup.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/gmemo.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/gtco.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/it.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/lazyutil.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/lispylet.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/llist.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/misc.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/__init__.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/client.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/common.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/msg.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/ptyproxy.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/server.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/net/util.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/numutil.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/regutil.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/seq.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/singleton.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/slicing.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/symbol.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/__init__.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/dbg.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/forall.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/ifexprs.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/letsyntax.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/nameutil.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/nb.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/prefix.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/simplelet.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/syntax/testingtools.py +0 -0
- {unpythonic-0.15.2 → unpythonic-0.15.5}/unpythonic/tco.py +0 -0
- {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.
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
Requires-Python:
|
|
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
|
-
       [](https://codecov.io/gh/Technologicat/unpythonic)
|
|
38
35
|
  
|
|
39
36
|
  [](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
|
-
|
|
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
|
|
630
|
-
y = letseq[[c
|
|
631
|
-
c
|
|
632
|
-
c
|
|
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
|
|
635
|
-
oddp
|
|
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
|
-
#
|
|
647
|
-
@dlet[x
|
|
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
|
|
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
|
|
662
|
-
local[b
|
|
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
|
|
787
|
-
g = lambda x: [local[y
|
|
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
|
-
##
|
|
840
|
-
|
|
841
|
-
**PyPI**
|
|
836
|
+
## Install & uninstall
|
|
842
837
|
|
|
843
|
-
|
|
838
|
+
### From PyPI
|
|
844
839
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
**GitHub**
|
|
840
|
+
```bash
|
|
841
|
+
pip install unpythonic
|
|
842
|
+
```
|
|
850
843
|
|
|
851
|
-
|
|
844
|
+
### From source
|
|
852
845
|
|
|
853
|
-
|
|
846
|
+
Clone the repo from GitHub. Then, navigate to it in a terminal, and:
|
|
854
847
|
|
|
855
|
-
|
|
848
|
+
```bash
|
|
849
|
+
pip install . --no-compile
|
|
850
|
+
```
|
|
856
851
|
|
|
857
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
858
|
+
This is a common issue when using macro expanders in Python.
|
|
864
859
|
|
|
865
|
-
|
|
860
|
+
### Uninstall
|
|
866
861
|
|
|
867
|
-
|
|
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
|
-
       [](https://codecov.io/gh/Technologicat/unpythonic)
|
|
6
6
|
  
|
|
7
7
|
  [](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
|
-
|
|
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
|
|
598
|
-
y = letseq[[c
|
|
599
|
-
c
|
|
600
|
-
c
|
|
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
|
|
603
|
-
oddp
|
|
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
|
-
#
|
|
615
|
-
@dlet[x
|
|
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
|
|
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
|
|
630
|
-
local[b
|
|
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
|
|
755
|
-
g = lambda x: [local[y
|
|
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
|
-
##
|
|
807
|
+
## Install & uninstall
|
|
808
808
|
|
|
809
|
-
|
|
809
|
+
### From PyPI
|
|
810
810
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
``sudo pip3 install unpythonic``
|
|
816
|
-
|
|
817
|
-
**GitHub**
|
|
811
|
+
```bash
|
|
812
|
+
pip install unpythonic
|
|
813
|
+
```
|
|
818
814
|
|
|
819
|
-
|
|
815
|
+
### From source
|
|
820
816
|
|
|
821
|
-
|
|
817
|
+
Clone the repo from GitHub. Then, navigate to it in a terminal, and:
|
|
822
818
|
|
|
823
|
-
|
|
819
|
+
```bash
|
|
820
|
+
pip install . --no-compile
|
|
821
|
+
```
|
|
824
822
|
|
|
825
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
829
|
+
This is a common issue when using macro expanders in Python.
|
|
832
830
|
|
|
833
|
-
|
|
831
|
+
### Uninstall
|
|
834
832
|
|
|
835
|
-
|
|
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.
|
|
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 * #
|
|
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.
|
|
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**:
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
166
|
-
|
|
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
|
-
|
|
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
|