aletk 0.1.4__tar.gz → 0.1.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.
- aletk-0.1.5/.gitignore +171 -0
- aletk-0.1.5/.gitlab-ci.yml +55 -0
- aletk-0.1.5/.python-version +1 -0
- {aletk-0.1.4 → aletk-0.1.5}/PKG-INFO +1 -1
- aletk-0.1.5/prototypes/MaybeMonad.py +131 -0
- aletk-0.1.5/prototypes/pipe.ipynb +211 -0
- aletk-0.1.5/prototypes/pipe.py +82 -0
- {aletk-0.1.4 → aletk-0.1.5}/pyproject.toml +7 -2
- aletk-0.1.5/src/aletk/_version.py +16 -0
- {aletk-0.1.4 → aletk-0.1.5}/src/aletk.egg-info/PKG-INFO +1 -1
- {aletk-0.1.4 → aletk-0.1.5}/src/aletk.egg-info/SOURCES.txt +9 -1
- aletk-0.1.5/tests/.gitkeep +0 -0
- {aletk-0.1.4 → aletk-0.1.5}/LICENSE +0 -0
- {aletk-0.1.4 → aletk-0.1.5}/README.md +0 -0
- {aletk-0.1.4 → aletk-0.1.5}/setup.cfg +0 -0
- {aletk-0.1.4 → aletk-0.1.5}/src/aletk/ResultMonad.py +0 -0
- {aletk-0.1.4 → aletk-0.1.5}/src/aletk/__init__.py +0 -0
- {aletk-0.1.4 → aletk-0.1.5}/src/aletk/adapters.py +0 -0
- {aletk-0.1.4 → aletk-0.1.5}/src/aletk/py.typed +0 -0
- {aletk-0.1.4 → aletk-0.1.5}/src/aletk/utils.py +0 -0
- {aletk-0.1.4 → aletk-0.1.5}/src/aletk.egg-info/dependency_links.txt +0 -0
- {aletk-0.1.4 → aletk-0.1.5}/src/aletk.egg-info/requires.txt +0 -0
- {aletk-0.1.4 → aletk-0.1.5}/src/aletk.egg-info/top_level.txt +0 -0
aletk-0.1.5/.gitignore
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
#uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
|
|
110
|
+
# pdm
|
|
111
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
112
|
+
#pdm.lock
|
|
113
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
114
|
+
# in version control.
|
|
115
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
116
|
+
.pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
121
|
+
__pypackages__/
|
|
122
|
+
|
|
123
|
+
# Celery stuff
|
|
124
|
+
celerybeat-schedule
|
|
125
|
+
celerybeat.pid
|
|
126
|
+
|
|
127
|
+
# SageMath parsed files
|
|
128
|
+
*.sage.py
|
|
129
|
+
|
|
130
|
+
# Environments
|
|
131
|
+
.env
|
|
132
|
+
.venv
|
|
133
|
+
env/
|
|
134
|
+
venv/
|
|
135
|
+
ENV/
|
|
136
|
+
env.bak/
|
|
137
|
+
venv.bak/
|
|
138
|
+
|
|
139
|
+
# Spyder project settings
|
|
140
|
+
.spyderproject
|
|
141
|
+
.spyproject
|
|
142
|
+
|
|
143
|
+
# Rope project settings
|
|
144
|
+
.ropeproject
|
|
145
|
+
|
|
146
|
+
# mkdocs documentation
|
|
147
|
+
/site
|
|
148
|
+
|
|
149
|
+
# mypy
|
|
150
|
+
.mypy_cache/
|
|
151
|
+
.dmypy.json
|
|
152
|
+
dmypy.json
|
|
153
|
+
|
|
154
|
+
# Pyre type checker
|
|
155
|
+
.pyre/
|
|
156
|
+
|
|
157
|
+
# pytype static type analyzer
|
|
158
|
+
.pytype/
|
|
159
|
+
|
|
160
|
+
# Cython debug symbols
|
|
161
|
+
cython_debug/
|
|
162
|
+
|
|
163
|
+
# PyCharm
|
|
164
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
165
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
166
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
167
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
168
|
+
#.idea/
|
|
169
|
+
|
|
170
|
+
# PyPI configuration file
|
|
171
|
+
.pypirc
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
stages:
|
|
2
|
+
- build
|
|
3
|
+
- publish
|
|
4
|
+
- release
|
|
5
|
+
|
|
6
|
+
workflow:
|
|
7
|
+
rules:
|
|
8
|
+
- if: $CI_COMMIT_TAG && $CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/
|
|
9
|
+
|
|
10
|
+
build:
|
|
11
|
+
stage: build
|
|
12
|
+
image: python:3-bookworm
|
|
13
|
+
script:
|
|
14
|
+
- python -m pip install -U build
|
|
15
|
+
- python -m build
|
|
16
|
+
artifacts:
|
|
17
|
+
paths:
|
|
18
|
+
- "dist/"
|
|
19
|
+
|
|
20
|
+
publish:
|
|
21
|
+
stage: publish
|
|
22
|
+
image: python:3-bookworm
|
|
23
|
+
dependencies:
|
|
24
|
+
- build
|
|
25
|
+
environment:
|
|
26
|
+
name: release
|
|
27
|
+
id_tokens:
|
|
28
|
+
PYPI_ID_TOKEN:
|
|
29
|
+
# Use "testpypi" if uploading to TestPyPI
|
|
30
|
+
aud: pypi
|
|
31
|
+
script:
|
|
32
|
+
# Install dependencies
|
|
33
|
+
- apt update && apt install -y jq
|
|
34
|
+
- python -m pip install -U twine id
|
|
35
|
+
|
|
36
|
+
# Retrieve the OIDC token from GitLab CI/CD, and exchange it for a PyPI API token
|
|
37
|
+
- oidc_token=$(python -m id PYPI)
|
|
38
|
+
# Replace "https://pypi.org/*" with "https://test.pypi.org/*" if uploading to TestPyPI
|
|
39
|
+
- resp=$(curl -X POST https://pypi.org/_/oidc/mint-token -d "{\"token\":\"${oidc_token}\"}")
|
|
40
|
+
- api_token=$(jq --raw-output '.token' <<< "${resp}")
|
|
41
|
+
|
|
42
|
+
# Upload to PyPI authenticating via the newly-minted token
|
|
43
|
+
# Add "--repository testpypi" if uploading to TestPyPI
|
|
44
|
+
- twine upload -u __token__ -p "${api_token}" dist/*
|
|
45
|
+
|
|
46
|
+
release:
|
|
47
|
+
stage: release
|
|
48
|
+
image: registry.gitlab.com/gitlab-org/release-cli:latest
|
|
49
|
+
dependencies:
|
|
50
|
+
- publish
|
|
51
|
+
script:
|
|
52
|
+
- echo "Running release-cli"
|
|
53
|
+
release:
|
|
54
|
+
tag_name: '$CI_COMMIT_TAG'
|
|
55
|
+
description: 'Release $CI_COMMIT_TAG'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Callable
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass(slots=True, frozen=True)
|
|
6
|
+
class Some[T]:
|
|
7
|
+
"""
|
|
8
|
+
Model for the success case of a function in this project.
|
|
9
|
+
|
|
10
|
+
Attributes
|
|
11
|
+
----------
|
|
12
|
+
value : T
|
|
13
|
+
The data returned by the function.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
value: T
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(slots=True, frozen=True)
|
|
20
|
+
class Nothing:
|
|
21
|
+
"""
|
|
22
|
+
Model for the error case of a function in this project.
|
|
23
|
+
|
|
24
|
+
Attributes
|
|
25
|
+
----------
|
|
26
|
+
message : str
|
|
27
|
+
A message describing the error.
|
|
28
|
+
code : int
|
|
29
|
+
A code that can be used to handle different error cases.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
type Maybe[T] = Some[T] | Nothing
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def mmap[T, U](f: Callable[[T], U], maybe: Some[T] | Nothing) -> Some[U] | Nothing:
|
|
39
|
+
"""
|
|
40
|
+
Map a function over a Maybe.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
f : Callable[[T], U]
|
|
45
|
+
The function to map over the Maybe.
|
|
46
|
+
maybe : Maybe[T]
|
|
47
|
+
The Maybe to map the function over.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
Maybe[U]
|
|
52
|
+
"""
|
|
53
|
+
match maybe:
|
|
54
|
+
case Some(value=value):
|
|
55
|
+
return Some(value=f(value))
|
|
56
|
+
case Nothing():
|
|
57
|
+
return maybe
|
|
58
|
+
case _:
|
|
59
|
+
raise ValueError("Invalid Maybe type.")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def mbind[T, U](f: Callable[[T], Some[U] | Nothing], maybe: Some[T] | Nothing) -> Some[U] | Nothing:
|
|
63
|
+
"""
|
|
64
|
+
Bind a function over a Maybe.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
f : Callable[[T], Some[U] | Nothing]
|
|
69
|
+
The function to bind over the Maybe.
|
|
70
|
+
maybe : Maybe[T]
|
|
71
|
+
The Maybe to bind the function over.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
Maybe[U]
|
|
76
|
+
"""
|
|
77
|
+
match maybe:
|
|
78
|
+
case Some(value):
|
|
79
|
+
return f(value)
|
|
80
|
+
case Nothing():
|
|
81
|
+
return maybe
|
|
82
|
+
case _:
|
|
83
|
+
raise ValueError("Invalid Maybe type.")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def munwrap[T](maybe: Some[T] | Nothing) -> T:
|
|
87
|
+
"""
|
|
88
|
+
Unwrap a Maybe.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
maybe : Maybe[T]
|
|
93
|
+
The Maybe to unwrap.
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
T
|
|
98
|
+
The value of the Maybe.
|
|
99
|
+
"""
|
|
100
|
+
match maybe:
|
|
101
|
+
case Some(value):
|
|
102
|
+
return value
|
|
103
|
+
case Nothing():
|
|
104
|
+
raise ValueError("Cannot unwrap a Nothing.")
|
|
105
|
+
case _:
|
|
106
|
+
raise ValueError("Invalid Maybe type.")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def munwrap_or[T](maybe: Some[T] | Nothing, default: T) -> T:
|
|
110
|
+
"""
|
|
111
|
+
Unwrap a Maybe or return a default value.
|
|
112
|
+
|
|
113
|
+
Parameters
|
|
114
|
+
----------
|
|
115
|
+
maybe : Maybe[T]
|
|
116
|
+
The Maybe to unwrap.
|
|
117
|
+
default : T
|
|
118
|
+
The default value to return if the Maybe is a Nothing.
|
|
119
|
+
|
|
120
|
+
Returns
|
|
121
|
+
-------
|
|
122
|
+
T
|
|
123
|
+
The value of the Maybe or the default value.
|
|
124
|
+
"""
|
|
125
|
+
match maybe:
|
|
126
|
+
case Some(value):
|
|
127
|
+
return value
|
|
128
|
+
case Nothing():
|
|
129
|
+
return default
|
|
130
|
+
case _:
|
|
131
|
+
raise ValueError("Invalid Maybe type.")
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"execution_count": 1,
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"outputs": [],
|
|
8
|
+
"source": [
|
|
9
|
+
"from pipe import pipe, pipe_out"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"cell_type": "code",
|
|
14
|
+
"execution_count": 2,
|
|
15
|
+
"metadata": {},
|
|
16
|
+
"outputs": [],
|
|
17
|
+
"source": [
|
|
18
|
+
"res = pipe(1) >> (lambda x: x + 1) >> (lambda x: x * 2) >> pipe_out"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"cell_type": "code",
|
|
23
|
+
"execution_count": 3,
|
|
24
|
+
"metadata": {},
|
|
25
|
+
"outputs": [
|
|
26
|
+
{
|
|
27
|
+
"data": {
|
|
28
|
+
"text/plain": [
|
|
29
|
+
"int"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"execution_count": 3,
|
|
33
|
+
"metadata": {},
|
|
34
|
+
"output_type": "execute_result"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"source": [
|
|
38
|
+
"res.__class__"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"cell_type": "code",
|
|
43
|
+
"execution_count": 4,
|
|
44
|
+
"metadata": {},
|
|
45
|
+
"outputs": [],
|
|
46
|
+
"source": [
|
|
47
|
+
"res = (pipe(3)\n",
|
|
48
|
+
" >> (lambda x: x + 1)\n",
|
|
49
|
+
" >> (lambda x: x * 2)\n",
|
|
50
|
+
" >> pipe_out\n",
|
|
51
|
+
" )\n"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"cell_type": "code",
|
|
56
|
+
"execution_count": 5,
|
|
57
|
+
"metadata": {},
|
|
58
|
+
"outputs": [
|
|
59
|
+
{
|
|
60
|
+
"data": {
|
|
61
|
+
"text/plain": [
|
|
62
|
+
"int"
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"execution_count": 5,
|
|
66
|
+
"metadata": {},
|
|
67
|
+
"output_type": "execute_result"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"source": [
|
|
71
|
+
"res.__class__"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"cell_type": "code",
|
|
76
|
+
"execution_count": 6,
|
|
77
|
+
"metadata": {},
|
|
78
|
+
"outputs": [],
|
|
79
|
+
"source": [
|
|
80
|
+
"from aletk.ResultMonad import try_except_wrapper, funwrap\n",
|
|
81
|
+
"from aletk.utils import get_logger\n",
|
|
82
|
+
"\n",
|
|
83
|
+
"lgr = get_logger(\"pipe test\")"
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"cell_type": "code",
|
|
88
|
+
"execution_count": 7,
|
|
89
|
+
"metadata": {},
|
|
90
|
+
"outputs": [],
|
|
91
|
+
"source": [
|
|
92
|
+
"g_error_msg = \"g error\"\n",
|
|
93
|
+
"h_error_msg = \"h error\"\n",
|
|
94
|
+
"\n",
|
|
95
|
+
"@try_except_wrapper(lgr)\n",
|
|
96
|
+
"def f(x):\n",
|
|
97
|
+
" return x + 1\n",
|
|
98
|
+
"\n",
|
|
99
|
+
"@try_except_wrapper(lgr)\n",
|
|
100
|
+
"def g(x):\n",
|
|
101
|
+
" raise ValueError(g_error_msg)\n",
|
|
102
|
+
"\n",
|
|
103
|
+
"@try_except_wrapper(lgr)\n",
|
|
104
|
+
"def h(x):\n",
|
|
105
|
+
" raise ValueError(h_error_msg)"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"cell_type": "code",
|
|
110
|
+
"execution_count": 8,
|
|
111
|
+
"metadata": {},
|
|
112
|
+
"outputs": [
|
|
113
|
+
{
|
|
114
|
+
"data": {
|
|
115
|
+
"text/plain": [
|
|
116
|
+
"3"
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"execution_count": 8,
|
|
120
|
+
"metadata": {},
|
|
121
|
+
"output_type": "execute_result"
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
"source": [
|
|
125
|
+
"pipe(1) >> funwrap(f) >> funwrap(f) >> pipe_out"
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"cell_type": "code",
|
|
130
|
+
"execution_count": 9,
|
|
131
|
+
"metadata": {},
|
|
132
|
+
"outputs": [
|
|
133
|
+
{
|
|
134
|
+
"name": "stderr",
|
|
135
|
+
"output_type": "stream",
|
|
136
|
+
"text": [
|
|
137
|
+
"2024-12-29 01:34:35 - pipe test - ERROR - An error occurred in function 'g'. Detail:\n",
|
|
138
|
+
"g error\n",
|
|
139
|
+
"2024-12-29 01:34:35 - pipe test - ERROR - An error occurred in function 'f'. Detail:\n",
|
|
140
|
+
"unsupported operand type(s) for +: 'Err' and 'int'\n"
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
"source": [
|
|
145
|
+
"res = pipe(1) >> f >> g >> f >> pipe_out"
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"cell_type": "code",
|
|
150
|
+
"execution_count": 10,
|
|
151
|
+
"metadata": {},
|
|
152
|
+
"outputs": [
|
|
153
|
+
{
|
|
154
|
+
"data": {
|
|
155
|
+
"text/plain": [
|
|
156
|
+
"Err(message=\"An error occurred in function 'f'. Detail: unsupported operand type(s) for +: 'Err' and 'int'\", code=-1)"
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
"execution_count": 10,
|
|
160
|
+
"metadata": {},
|
|
161
|
+
"output_type": "execute_result"
|
|
162
|
+
}
|
|
163
|
+
],
|
|
164
|
+
"source": [
|
|
165
|
+
"res"
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"cell_type": "code",
|
|
170
|
+
"execution_count": 12,
|
|
171
|
+
"metadata": {},
|
|
172
|
+
"outputs": [
|
|
173
|
+
{
|
|
174
|
+
"ename": "AssertionError",
|
|
175
|
+
"evalue": "TODO: should return the error thrown by 'g' ('g error'), as it is the first error in the chain and errors should propagate. Failed with:\n\t \"g error\" != \"An error occurred in function 'f'. Detail: unsupported operand type(s) for +: 'Err' and 'int'\"",
|
|
176
|
+
"output_type": "error",
|
|
177
|
+
"traceback": [
|
|
178
|
+
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
|
179
|
+
"\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)",
|
|
180
|
+
"Cell \u001b[0;32mIn[12], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m res\u001b[38;5;241m.\u001b[39mmessage \u001b[38;5;241m==\u001b[39m g_error_msg, \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mTODO: should return the error thrown by \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mg\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m (\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mg_error_msg\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m), as it is the first error in the chain and errors should propagate. Failed with:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00mg_error_msg\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124m != \u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00mres\u001b[38;5;241m.\u001b[39mmessage\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n",
|
|
181
|
+
"\u001b[0;31mAssertionError\u001b[0m: TODO: should return the error thrown by 'g' ('g error'), as it is the first error in the chain and errors should propagate. Failed with:\n\t \"g error\" != \"An error occurred in function 'f'. Detail: unsupported operand type(s) for +: 'Err' and 'int'\""
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
],
|
|
185
|
+
"source": [
|
|
186
|
+
"assert res.message == g_error_msg, f\"TODO: should return the error thrown by 'g' ('{g_error_msg}'), as it is the first error in the chain and errors should propagate. Failed with:\\n\\t \\\"{g_error_msg}\\\" != \\\"{res.message}\\\"\"\n"
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
],
|
|
190
|
+
"metadata": {
|
|
191
|
+
"kernelspec": {
|
|
192
|
+
"display_name": ".venv",
|
|
193
|
+
"language": "python",
|
|
194
|
+
"name": "python3"
|
|
195
|
+
},
|
|
196
|
+
"language_info": {
|
|
197
|
+
"codemirror_mode": {
|
|
198
|
+
"name": "ipython",
|
|
199
|
+
"version": 3
|
|
200
|
+
},
|
|
201
|
+
"file_extension": ".py",
|
|
202
|
+
"mimetype": "text/x-python",
|
|
203
|
+
"name": "python",
|
|
204
|
+
"nbconvert_exporter": "python",
|
|
205
|
+
"pygments_lexer": "ipython3",
|
|
206
|
+
"version": "3.13.0"
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"nbformat": 4,
|
|
210
|
+
"nbformat_minor": 2
|
|
211
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
### TO DELETE IF THIS EVER GETS IMPLEMENTED IN THE MAIN LIBRARY ###
|
|
2
|
+
import sys
|
|
3
|
+
sys.path.append('..')
|
|
4
|
+
### ###
|
|
5
|
+
|
|
6
|
+
from typing import Callable
|
|
7
|
+
|
|
8
|
+
from src.aletk.ResultMonad import Err, Ok
|
|
9
|
+
|
|
10
|
+
class pipe[T]:
|
|
11
|
+
"""
|
|
12
|
+
A pipe object that can be used to chain functions together with the `>>` operator.
|
|
13
|
+
|
|
14
|
+
### Example
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from aletk import pipe
|
|
18
|
+
|
|
19
|
+
def add_one(x: int) -> int:
|
|
20
|
+
return x + 1
|
|
21
|
+
|
|
22
|
+
def multiply_by_two(x: int) -> int:
|
|
23
|
+
return x * 2
|
|
24
|
+
|
|
25
|
+
result = pipe(1) >> add_one >> multiply_by_two
|
|
26
|
+
print(result.output) # 4
|
|
27
|
+
``
|
|
28
|
+
"""
|
|
29
|
+
def __init__(self, value: T):
|
|
30
|
+
self.value = value
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def output(self) -> T:
|
|
34
|
+
return self.value
|
|
35
|
+
|
|
36
|
+
def __rshift__[U](
|
|
37
|
+
self, func: Callable[[T | 'pipe[T]'], U | Ok[U]]
|
|
38
|
+
) -> 'pipe[U]' | U | 'pipe[T]' | Err | 'pipe[Err]' | Ok[U]:
|
|
39
|
+
if func is pipe_out:
|
|
40
|
+
# If the function is `pipe_out`, call it directly on `self`
|
|
41
|
+
return func(self)
|
|
42
|
+
|
|
43
|
+
if isinstance(self.value, Err):
|
|
44
|
+
# Propagate Err without calling the function
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
if isinstance(self.value, Ok):
|
|
48
|
+
# Unwrap the Ok value
|
|
49
|
+
unwrapped_value = self.value.out
|
|
50
|
+
else:
|
|
51
|
+
# Regular value
|
|
52
|
+
unwrapped_value = self.value
|
|
53
|
+
|
|
54
|
+
# Call the function
|
|
55
|
+
result = func(unwrapped_value)
|
|
56
|
+
|
|
57
|
+
if isinstance(result, Err):
|
|
58
|
+
# Propagate Err if returned by the function
|
|
59
|
+
return pipe(result)
|
|
60
|
+
elif isinstance(result, Ok):
|
|
61
|
+
# Wrap Ok result in pipe
|
|
62
|
+
return pipe(result) # type: ignore
|
|
63
|
+
else:
|
|
64
|
+
# Wrap regular result in pipe
|
|
65
|
+
return pipe(result)
|
|
66
|
+
|
|
67
|
+
def __or__[U](self, func: Callable[[T | 'pipe[T]'], U]) -> 'pipe[U]' | U:
|
|
68
|
+
if func is pipe_out:
|
|
69
|
+
return func(self)
|
|
70
|
+
return pipe(func(self.value))
|
|
71
|
+
|
|
72
|
+
def __repr__(self) -> str:
|
|
73
|
+
return f'{self.value}'
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def pipe_out[T](pipe_result: pipe[T] | pipe[Ok[T]] | T) -> T:
|
|
77
|
+
if isinstance(pipe_result, pipe):
|
|
78
|
+
if isinstance(pipe_result.output, Ok):
|
|
79
|
+
return pipe_result.output.out
|
|
80
|
+
|
|
81
|
+
return pipe_result.output # Return the unwrapped result
|
|
82
|
+
return pipe_result
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=75.6.0", "wheel"]
|
|
2
|
+
requires = ["setuptools>=75.6.0", "setuptools_scm>=8.1", "wheel"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "aletk"
|
|
7
7
|
description = "Collection of general purpose tools to work with Python"
|
|
8
|
-
version = "0.1.4"
|
|
9
8
|
requires-python = ">=3.13"
|
|
10
9
|
authors = [
|
|
11
10
|
{name = "Luis Alejandro Bordo García", email = "bgluiszz@gmail.com"},
|
|
@@ -24,6 +23,12 @@ dependencies = [
|
|
|
24
23
|
"fuzzywuzzy",
|
|
25
24
|
"python-Levenshtein"
|
|
26
25
|
]
|
|
26
|
+
dynamic = ["version"]
|
|
27
|
+
|
|
28
|
+
[tool.setuptools_scm]
|
|
29
|
+
version_scheme = "guess-next-dev"
|
|
30
|
+
local_scheme = "no-local-version"
|
|
31
|
+
write_to = "src/aletk/_version.py"
|
|
27
32
|
|
|
28
33
|
[project.urls]
|
|
29
34
|
Repository = "https://gitlab.com/alebg/aletk"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file generated by setuptools_scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
TYPE_CHECKING = False
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from typing import Tuple, Union
|
|
6
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
|
+
else:
|
|
8
|
+
VERSION_TUPLE = object
|
|
9
|
+
|
|
10
|
+
version: str
|
|
11
|
+
__version__: str
|
|
12
|
+
__version_tuple__: VERSION_TUPLE
|
|
13
|
+
version_tuple: VERSION_TUPLE
|
|
14
|
+
|
|
15
|
+
__version__ = version = '0.1.5'
|
|
16
|
+
__version_tuple__ = version_tuple = (0, 1, 5)
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
.gitignore
|
|
2
|
+
.gitlab-ci.yml
|
|
3
|
+
.python-version
|
|
1
4
|
LICENSE
|
|
2
5
|
README.md
|
|
3
6
|
pyproject.toml
|
|
7
|
+
prototypes/MaybeMonad.py
|
|
8
|
+
prototypes/pipe.ipynb
|
|
9
|
+
prototypes/pipe.py
|
|
4
10
|
src/aletk/ResultMonad.py
|
|
5
11
|
src/aletk/__init__.py
|
|
12
|
+
src/aletk/_version.py
|
|
6
13
|
src/aletk/adapters.py
|
|
7
14
|
src/aletk/py.typed
|
|
8
15
|
src/aletk/utils.py
|
|
@@ -10,4 +17,5 @@ src/aletk.egg-info/PKG-INFO
|
|
|
10
17
|
src/aletk.egg-info/SOURCES.txt
|
|
11
18
|
src/aletk.egg-info/dependency_links.txt
|
|
12
19
|
src/aletk.egg-info/requires.txt
|
|
13
|
-
src/aletk.egg-info/top_level.txt
|
|
20
|
+
src/aletk.egg-info/top_level.txt
|
|
21
|
+
tests/.gitkeep
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|