pythonic-fp.containers 4.0.0__tar.gz → 4.1.0__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.
- pythonic_fp_containers-4.1.0/.github/workflows/static.yml +64 -0
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/.gitignore +2 -0
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/CHANGELOG.rst +18 -0
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/PKG-INFO +15 -15
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/README.rst +6 -6
- pythonic_fp_containers-4.1.0/docs/Makefile +78 -0
- pythonic_fp_containers-4.1.0/docs/gen_conf.py +110 -0
- pythonic_fp_containers-4.1.0/docs/requirements.txt +2 -0
- pythonic_fp_containers-4.1.0/docs/source/_static/custom.css +3 -0
- pythonic_fp_containers-4.1.0/docs/source/api/functional_tuple.rst +6 -0
- pythonic_fp_containers-4.1.0/docs/source/api/immutable_list.rst +6 -0
- pythonic_fp_containers-4.1.0/docs/source/api/index.rst +19 -0
- pythonic_fp_containers-4.1.0/docs/source/changelog.rst +7 -0
- pythonic_fp_containers-4.1.0/docs/source/description.rst +7 -0
- pythonic_fp_containers-4.1.0/docs/source/index.rst +30 -0
- pythonic_fp_containers-4.1.0/docs/source/releases.rst +22 -0
- pythonic_fp_containers-4.1.0/docs/source/usage.rst +21 -0
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/pyproject.toml +24 -20
- pythonic_fp_containers-4.1.0/src/pythonic_fp/containers/__init__.py +30 -0
- pythonic_fp_containers-4.1.0/src/pythonic_fp/containers/__init__.pyi +0 -0
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/src/pythonic_fp/containers/functional_tuple.py +89 -43
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/src/pythonic_fp/containers/immutable_list.py +157 -50
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/src/pythonic_fp/containers/immutable_list.pyi +1 -1
- pythonic_fp_containers-4.1.0/src/pythonic_fp/containers/py.typed +0 -0
- pythonic_fp_containers-4.0.0/src/pythonic_fp/containers/__init__.py +0 -31
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/LICENSE +0 -0
- /pythonic_fp_containers-4.0.0/src/pythonic_fp/containers/__init__.pyi → /pythonic_fp_containers-4.1.0/docs/source/_static/.gitkeep +0 -0
- /pythonic_fp_containers-4.0.0/src/pythonic_fp/containers/py.typed → /pythonic_fp_containers-4.1.0/docs/source/_templates/.gitkeep +0 -0
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/src/pythonic_fp/containers/functional_tuple.pyi +0 -0
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/tests/test_functional_tuple.py +0 -0
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/tests/test_immutable_list.py +0 -0
- {pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/tests/test_immutable_list_fp.py +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: Build and Deploy Furo Themed Sphinx Documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
env:
|
|
9
|
+
RELEASE: '4.1.0'
|
|
10
|
+
DEVEL: '5.0.0'
|
|
11
|
+
PYTHON: '3.14'
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
pages: write
|
|
16
|
+
id-token: write
|
|
17
|
+
|
|
18
|
+
concurrency:
|
|
19
|
+
group: "pages"
|
|
20
|
+
cancel-in-progress: false
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
deploy:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
environment:
|
|
26
|
+
name: github-pages
|
|
27
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout
|
|
30
|
+
uses: actions/checkout@v6
|
|
31
|
+
|
|
32
|
+
- name: Select Python
|
|
33
|
+
uses: actions/setup-python@v6
|
|
34
|
+
with:
|
|
35
|
+
python-version: ${{ env.PYTHON }}
|
|
36
|
+
|
|
37
|
+
- name: Install Sphinx Dependencies
|
|
38
|
+
run: |
|
|
39
|
+
pip install -r docs/requirements.txt
|
|
40
|
+
sudo apt-get install -y graphviz
|
|
41
|
+
|
|
42
|
+
- name: Build Release Docs
|
|
43
|
+
run: |
|
|
44
|
+
pip install pythonic-fp-circulararray
|
|
45
|
+
docs/gen_conf.py release ${{ env.RELEASE }} Containers containers > docs/source/conf.py
|
|
46
|
+
sphinx-build -M html docs/source docs/build/release
|
|
47
|
+
|
|
48
|
+
- name: Build Development Docs
|
|
49
|
+
run: |
|
|
50
|
+
pip install -e .
|
|
51
|
+
docs/gen_conf.py devel ${{ env.DEVEL }} Containers containers > docs/source/conf.py
|
|
52
|
+
sphinx-build -M html docs/source docs/build/development
|
|
53
|
+
|
|
54
|
+
- name: Setup Pages
|
|
55
|
+
uses: actions/configure-pages@v6
|
|
56
|
+
|
|
57
|
+
- name: Upload Artifact
|
|
58
|
+
uses: actions/upload-pages-artifact@v5
|
|
59
|
+
with:
|
|
60
|
+
path: docs/build
|
|
61
|
+
|
|
62
|
+
- name: Deploy to GitHub Pages
|
|
63
|
+
uses: actions/deploy-pages@v5
|
|
64
|
+
id: deployment
|
|
@@ -17,6 +17,24 @@ See `Semantic Versioning 2.0.0 <https://semver.org>`_.
|
|
|
17
17
|
Releases and Important Milestones
|
|
18
18
|
---------------------------------
|
|
19
19
|
|
|
20
|
+
PyPI 4.1.0 - 2026-05-14
|
|
21
|
+
~~~~~~~~~~~~~~~~~~~~~~~
|
|
22
|
+
|
|
23
|
+
Documentation now in maintenance mode.
|
|
24
|
+
|
|
25
|
+
Intend to make these containers final instead of inheritable.
|
|
26
|
+
|
|
27
|
+
Development Status Reappraisal - 2026-05-05
|
|
28
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
29
|
+
|
|
30
|
+
Maintainer appraised the Development Status for
|
|
31
|
+
pythonic-fp-containers to be ``"3 - Alpha"``.
|
|
32
|
+
|
|
33
|
+
PyPI 4.0.1 - 2026-01-19
|
|
34
|
+
~~~~~~~~~~~~~~~~~~~~~~~
|
|
35
|
+
|
|
36
|
+
Docstring improvements.
|
|
37
|
+
|
|
20
38
|
PyPI 4.0.0 - 2025-09-09
|
|
21
39
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
22
40
|
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonic-fp-containers
|
|
3
|
-
Version: 4.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 4.1.0
|
|
4
|
+
Summary: Containers with FP interfaces
|
|
5
5
|
Keywords: functional_containers,functional_tuple,immutable_list
|
|
6
6
|
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
8
|
Description-Content-Type: text/x-rst
|
|
9
9
|
Classifier: Development Status :: 3 - Alpha
|
|
10
10
|
Classifier: Framework :: Pytest
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
15
|
Classifier: Typing :: Typed
|
|
16
16
|
License-File: LICENSE
|
|
17
|
-
Requires-Dist: pythonic-fp-circulararray>=6.0.
|
|
18
|
-
Requires-Dist: pythonic-fp-fptools>=5.
|
|
19
|
-
Requires-Dist: pythonic-fp-iterables>=5.1.
|
|
17
|
+
Requires-Dist: pythonic-fp-circulararray>=6.0.4
|
|
18
|
+
Requires-Dist: pythonic-fp-fptools>=5.2.0
|
|
19
|
+
Requires-Dist: pythonic-fp-iterables>=5.1.3
|
|
20
20
|
Requires-Dist: pytest>=8.4.1 ; extra == "test"
|
|
21
21
|
Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-containers/blob/main/CHANGELOG.rst
|
|
22
|
-
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/containers
|
|
23
|
-
Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/
|
|
22
|
+
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/projects/containers.html
|
|
23
|
+
Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/
|
|
24
24
|
Project-URL: Source, https://github.com/grscheller/pythonic-fp-containers
|
|
25
25
|
Provides-Extra: test
|
|
26
26
|
|
|
@@ -28,8 +28,8 @@ Pythonic FP - Containers
|
|
|
28
28
|
========================
|
|
29
29
|
|
|
30
30
|
PyPI project
|
|
31
|
-
`pythonic-fp
|
|
32
|
-
<https://pypi.org/project/pythonic-fp
|
|
31
|
+
`pythonic-fp-containers
|
|
32
|
+
<https://pypi.org/project/pythonic-fp-containers>`_.
|
|
33
33
|
|
|
34
34
|
Python package of container like data structures with functional
|
|
35
35
|
programming inspired tweaks.
|
|
@@ -44,19 +44,19 @@ programming inspired tweaks.
|
|
|
44
44
|
|
|
45
45
|
Part of the
|
|
46
46
|
`pythonic-fp
|
|
47
|
-
<https://grscheller.github.io/pythonic-fp
|
|
47
|
+
<https://grscheller.github.io/pythonic-fp/>`_
|
|
48
48
|
PyPI projects.
|
|
49
49
|
|
|
50
50
|
Documentation
|
|
51
51
|
-------------
|
|
52
52
|
|
|
53
|
-
Documentation for this project
|
|
53
|
+
Documentation and other links for this project are hosted on
|
|
54
54
|
`GitHub Pages
|
|
55
|
-
<https://grscheller.github.io/pythonic-fp/containers
|
|
55
|
+
<https://grscheller.github.io/pythonic-fp/projects/containers.html>`_.
|
|
56
56
|
|
|
57
57
|
Copyright and License
|
|
58
58
|
---------------------
|
|
59
59
|
|
|
60
|
-
Copyright (c) 2023-
|
|
60
|
+
Copyright (c) 2023-2026 Geoffrey R. Scheller. Licensed under the Apache
|
|
61
61
|
License, Version 2.0. See the LICENSE file for details.
|
|
62
62
|
|
|
@@ -2,8 +2,8 @@ Pythonic FP - Containers
|
|
|
2
2
|
========================
|
|
3
3
|
|
|
4
4
|
PyPI project
|
|
5
|
-
`pythonic-fp
|
|
6
|
-
<https://pypi.org/project/pythonic-fp
|
|
5
|
+
`pythonic-fp-containers
|
|
6
|
+
<https://pypi.org/project/pythonic-fp-containers>`_.
|
|
7
7
|
|
|
8
8
|
Python package of container like data structures with functional
|
|
9
9
|
programming inspired tweaks.
|
|
@@ -18,18 +18,18 @@ programming inspired tweaks.
|
|
|
18
18
|
|
|
19
19
|
Part of the
|
|
20
20
|
`pythonic-fp
|
|
21
|
-
<https://grscheller.github.io/pythonic-fp
|
|
21
|
+
<https://grscheller.github.io/pythonic-fp/>`_
|
|
22
22
|
PyPI projects.
|
|
23
23
|
|
|
24
24
|
Documentation
|
|
25
25
|
-------------
|
|
26
26
|
|
|
27
|
-
Documentation for this project
|
|
27
|
+
Documentation and other links for this project are hosted on
|
|
28
28
|
`GitHub Pages
|
|
29
|
-
<https://grscheller.github.io/pythonic-fp/containers
|
|
29
|
+
<https://grscheller.github.io/pythonic-fp/projects/containers.html>`_.
|
|
30
30
|
|
|
31
31
|
Copyright and License
|
|
32
32
|
---------------------
|
|
33
33
|
|
|
34
|
-
Copyright (c) 2023-
|
|
34
|
+
Copyright (c) 2023-2026 Geoffrey R. Scheller. Licensed under the Apache
|
|
35
35
|
License, Version 2.0. See the LICENSE file for details.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Makefile for Sphinx documentation
|
|
2
|
+
|
|
3
|
+
# Last and future releases
|
|
4
|
+
# - former needs to agree with PyPI
|
|
5
|
+
# - later needs to agree with pyproject.toml
|
|
6
|
+
PROJECT_NAME = Containers
|
|
7
|
+
PYPI_NAME = containers
|
|
8
|
+
RELEASE_VERSION = 4.1.0
|
|
9
|
+
DEVEL_VERSION = 5.0.0
|
|
10
|
+
CUSTOM_VERSION = 0.0.0
|
|
11
|
+
|
|
12
|
+
SPHINXOPTS ?=
|
|
13
|
+
SPHINXBUILD ?= sphinx-build
|
|
14
|
+
SOURCEDIR = source
|
|
15
|
+
BUILDDIR = build
|
|
16
|
+
|
|
17
|
+
STATICDIR = source/_static
|
|
18
|
+
TEMPLATEDIR = source/_templates
|
|
19
|
+
|
|
20
|
+
# Put help first so that "make" without argument is like "make help".
|
|
21
|
+
help:
|
|
22
|
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS)
|
|
23
|
+
|
|
24
|
+
clean_source:
|
|
25
|
+
@rm -f "$(SOURCEDIR)"/conf.py
|
|
26
|
+
|
|
27
|
+
clean_devel:
|
|
28
|
+
@rm -rf "$(BUILDDIR)"/development
|
|
29
|
+
|
|
30
|
+
clean_release:
|
|
31
|
+
@rm -rf "$(BUILDDIR)"/release
|
|
32
|
+
|
|
33
|
+
clean_custom:
|
|
34
|
+
@rm -rf "$(BUILDDIR)"/custom
|
|
35
|
+
|
|
36
|
+
clean: clean_devel clean_release clean_custom clean_source
|
|
37
|
+
@rm -rf $(BUILDDIR)
|
|
38
|
+
@echo Removed all doc build artifacts.
|
|
39
|
+
|
|
40
|
+
setup_devel: clean_devel
|
|
41
|
+
@./gen_conf.py devel "$(DEVEL_VERSION)" "$(PROJECT_NAME)" "$(PYPI_NAME)" > "$(SOURCEDIR)"/conf.py
|
|
42
|
+
|
|
43
|
+
setup_release: clean_release
|
|
44
|
+
@./gen_conf.py release "$(RELEASE_VERSION)" "$(PROJECT_NAME)" "$(PYPI_NAME)" > "$(SOURCEDIR)"/conf.py
|
|
45
|
+
|
|
46
|
+
setup_custom: clean_custom
|
|
47
|
+
@./gen_conf.py custom "$(CUSTOM_VERSION)" "$(PROJECT_NAME)" "$(PYPI_NAME)" > "$(SOURCEDIR)"/conf.py
|
|
48
|
+
|
|
49
|
+
setup_redo: clean_custom
|
|
50
|
+
@echo Redoing custom build with a possibly reeditted conf.py
|
|
51
|
+
@./gen_conf.py redo "" "" ""
|
|
52
|
+
|
|
53
|
+
# Build development labeled html docs.
|
|
54
|
+
devel: Makefile setup_devel
|
|
55
|
+
@$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)"/development $(SPHINXOPTS)
|
|
56
|
+
|
|
57
|
+
# Build release labeled html docs.
|
|
58
|
+
release: Makefile setup_release
|
|
59
|
+
@$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)"/release $(SPHINXOPTS)
|
|
60
|
+
|
|
61
|
+
# Build custom labeled html docs.
|
|
62
|
+
custom: Makefile setup_custom
|
|
63
|
+
@$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)"/custom $(SPHINXOPTS)
|
|
64
|
+
|
|
65
|
+
# Build HTML docs with previous generated (possibly hand edited) source/conf.py,
|
|
66
|
+
# put results in $(BUILDDIR)/custom.
|
|
67
|
+
redo: Makefile setup_redo
|
|
68
|
+
@$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)"/custom $(SPHINXOPTS)
|
|
69
|
+
|
|
70
|
+
# Catch-all target: route all unknown targets to Sphinx and save to $(BUILDDIR)/custom.
|
|
71
|
+
# Reuse a previously generated source/conf.py.
|
|
72
|
+
%: Makefile setup_redo
|
|
73
|
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)"/custom $(SPHINXOPTS)
|
|
74
|
+
|
|
75
|
+
.PHONY: help Makefile
|
|
76
|
+
.PHONY: setup_devel setup_release setup_custom
|
|
77
|
+
.PHONY: devel release
|
|
78
|
+
.PHONY: clean_source clean_devel clean_release clean_custom clean
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# Generate conf.py Sphinx configuration executable
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
project_prefix = 'Pythonic FP'
|
|
8
|
+
pypi_prefix = 'pythonic-fp'
|
|
9
|
+
author = 'Geoffrey R. Scheller'
|
|
10
|
+
copyright = f'2023-2026, {author}'
|
|
11
|
+
|
|
12
|
+
args = sys.argv[1:]
|
|
13
|
+
num_args = len(args)
|
|
14
|
+
|
|
15
|
+
if num_args == 4:
|
|
16
|
+
build_type, version, project_name, pypi_name = args
|
|
17
|
+
project = project_prefix + ' - ' + project_name
|
|
18
|
+
pypi_project_repo = pypi_prefix + '-' + pypi_name
|
|
19
|
+
project_url_rst = f'`{project} <https://pypi.org/project/{pypi_project_repo}/>`_'
|
|
20
|
+
homepage_url_rst = f'`{project_prefix} <https://grscheller.github.io/{pypi_prefix}/>`_'
|
|
21
|
+
|
|
22
|
+
else:
|
|
23
|
+
sys.exit('Error: gen_conf.py takes exactly 4 arguments')
|
|
24
|
+
|
|
25
|
+
match build_type:
|
|
26
|
+
case 'custom':
|
|
27
|
+
release = f'{version}'
|
|
28
|
+
release_string = f'**Custom PyPI Non-release version {release}**'
|
|
29
|
+
case 'devel':
|
|
30
|
+
release = f'{version}'
|
|
31
|
+
release_string = f'**Proposed PyPI release version {release}**'
|
|
32
|
+
case 'release':
|
|
33
|
+
release = f'{version}'
|
|
34
|
+
release_string = f'**PyPI release version {release}**'
|
|
35
|
+
case 'redo':
|
|
36
|
+
if not os.path.isfile('source/conf.py'):
|
|
37
|
+
sys.exit('Error: cannot redo without source/conf.py')
|
|
38
|
+
sys.exit()
|
|
39
|
+
case _:
|
|
40
|
+
sys.exit(f'Error: unknown built_type {build_type} given')
|
|
41
|
+
|
|
42
|
+
print(f'''# Generated conf.py Sphinx configuration executable
|
|
43
|
+
|
|
44
|
+
from typing import Any
|
|
45
|
+
from sphinx.application import Sphinx
|
|
46
|
+
|
|
47
|
+
project = '{project}'
|
|
48
|
+
author = '{author}'
|
|
49
|
+
copyright = '{copyright}'
|
|
50
|
+
release = '{release}'
|
|
51
|
+
release_string = '{release_string}'
|
|
52
|
+
|
|
53
|
+
def skip_abc_methods(
|
|
54
|
+
app: Any, what: str, name: str, obj: Any, skip: bool, options: Any
|
|
55
|
+
) -> bool:
|
|
56
|
+
if name in [
|
|
57
|
+
'__init_subclass__',
|
|
58
|
+
'__subclasshook__',
|
|
59
|
+
'__class_getitem__',
|
|
60
|
+
'__weakref__',
|
|
61
|
+
]:
|
|
62
|
+
return True # Skip these members
|
|
63
|
+
return skip
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def setup(app: Sphinx) -> None:
|
|
67
|
+
app.connect('autodoc-skip-member', skip_abc_methods)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
extensions = [
|
|
71
|
+
'sphinx.ext.autodoc',
|
|
72
|
+
'sphinx.ext.graphviz',
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
autodoc_default_options = {{
|
|
76
|
+
'members': True,
|
|
77
|
+
'private-members': True,
|
|
78
|
+
'special-members': True,
|
|
79
|
+
'inherited-members': False,
|
|
80
|
+
'show-inheritance': True,
|
|
81
|
+
}}
|
|
82
|
+
autodoc_member_order = 'bysource'
|
|
83
|
+
autoclass_content = 'class'
|
|
84
|
+
autodoc_class_signature = 'separated'
|
|
85
|
+
autodoc_typehints_format = 'short'
|
|
86
|
+
autodoc_use_type_comments = True
|
|
87
|
+
autodoc_docstring_signature = False
|
|
88
|
+
autodoc_preserve_defaults = True
|
|
89
|
+
autodoc_warningiserror = False
|
|
90
|
+
|
|
91
|
+
templates_path = ['_templates']
|
|
92
|
+
exclude_patterns: list[str] = []
|
|
93
|
+
|
|
94
|
+
html_theme_options = {{
|
|
95
|
+
'light_css_variables': {{
|
|
96
|
+
'color-link--visited': 'var(--color-link)',
|
|
97
|
+
}},
|
|
98
|
+
'dark_css_variables': {{
|
|
99
|
+
'color-link--visited': 'var(--color-link)',
|
|
100
|
+
}},
|
|
101
|
+
}}
|
|
102
|
+
html_theme = 'furo'
|
|
103
|
+
html_static_path = ['_static']
|
|
104
|
+
html_css_files = ['custom.css']
|
|
105
|
+
|
|
106
|
+
rst_epilog = """
|
|
107
|
+
.. |RELEASE_STRING| replace:: {release_string}
|
|
108
|
+
|
|
109
|
+
"""
|
|
110
|
+
''')
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Containers
|
|
2
|
+
==========
|
|
3
|
+
|
|
4
|
+
.. automodule:: pythonic_fp.containers
|
|
5
|
+
:no-members:
|
|
6
|
+
:ignore-module-all:
|
|
7
|
+
:no-index:
|
|
8
|
+
|
|
9
|
+
.. toctree::
|
|
10
|
+
:caption: Functional Tuple
|
|
11
|
+
:maxdepth: 1
|
|
12
|
+
|
|
13
|
+
functional_tuple
|
|
14
|
+
|
|
15
|
+
.. toctree::
|
|
16
|
+
:caption: Immutable List
|
|
17
|
+
:maxdepth: 1
|
|
18
|
+
|
|
19
|
+
immutable_list
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
pythonic-fp-containers
|
|
2
|
+
======================
|
|
3
|
+
|
|
4
|
+
Project
|
|
5
|
+
`Pythonic FP - Containers <https://pypi.org/project/pythonic-fp-containers/>`_
|
|
6
|
+
one of the
|
|
7
|
+
`Pythonic FP <https://grscheller.github.io/pythonic-fp/>`_
|
|
8
|
+
PyPI projects.
|
|
9
|
+
|
|
10
|
+
|RELEASE_STRING|
|
|
11
|
+
|
|
12
|
+
.. toctree::
|
|
13
|
+
:caption: Overview
|
|
14
|
+
:hidden:
|
|
15
|
+
|
|
16
|
+
self
|
|
17
|
+
|
|
18
|
+
.. toctree::
|
|
19
|
+
:caption: User Documentation
|
|
20
|
+
|
|
21
|
+
description
|
|
22
|
+
usage
|
|
23
|
+
releases
|
|
24
|
+
changelog
|
|
25
|
+
|
|
26
|
+
.. toctree::
|
|
27
|
+
:caption: API Documentation
|
|
28
|
+
:maxdepth: 1
|
|
29
|
+
|
|
30
|
+
api/index
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
PyPI releases
|
|
2
|
+
=============
|
|
3
|
+
|
|
4
|
+
**pythonic-fp-containers:** pythonic_fp.containers
|
|
5
|
+
|
|
6
|
+
+----------------------------------------------------------------------------------------+--------------+
|
|
7
|
+
| PyPI Release | Release date |
|
|
8
|
+
+========================================================================================+==============+
|
|
9
|
+
| `development <https://grscheller.github.io/pythonic-fp-containers/development/html/>`_ | |
|
|
10
|
+
+----------------------------------------------------------------------------------------+--------------+
|
|
11
|
+
| `v4.1.0 <https://grscheller.github.io/pythonic-fp-containers/release/html/>`_ | 2026-05-14 |
|
|
12
|
+
+----------------------------------------------------------------------------------------+--------------+
|
|
13
|
+
| v4.0.1 | 2026-01-18 |
|
|
14
|
+
+----------------------------------------------------------------------------------------+--------------+
|
|
15
|
+
| v4.0.0 | 2025-09-28 |
|
|
16
|
+
+----------------------------------------------------------------------------------------+--------------+
|
|
17
|
+
| v3.0.1 | 2025-09-09 |
|
|
18
|
+
+----------------------------------------------------------------------------------------+--------------+
|
|
19
|
+
| v3.0.0 | 2025-08-02 |
|
|
20
|
+
+----------------------------------------------------------------------------------------+--------------+
|
|
21
|
+
| v2.0.0 | 2025-07-06 |
|
|
22
|
+
+----------------------------------------------------------------------------------------+--------------+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Usage
|
|
2
|
+
=====
|
|
3
|
+
|
|
4
|
+
How to install the package
|
|
5
|
+
--------------------------
|
|
6
|
+
|
|
7
|
+
Install the project into your Python environment:
|
|
8
|
+
|
|
9
|
+
.. code:: console
|
|
10
|
+
|
|
11
|
+
$ pip install pythonic-fp.containers
|
|
12
|
+
|
|
13
|
+
Importing the package
|
|
14
|
+
---------------------
|
|
15
|
+
|
|
16
|
+
Import the functional container classes into your code.
|
|
17
|
+
|
|
18
|
+
.. code:: python
|
|
19
|
+
|
|
20
|
+
from pythonic_fp.containers.functional_tuple import FTuple
|
|
21
|
+
from pythonic_fp.containers.immutable_list import IList
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["flit_core>=3.12,<4"]
|
|
3
|
+
build-backend = "flit_core.buildapi"
|
|
4
|
+
|
|
1
5
|
[project]
|
|
2
6
|
name = "pythonic-fp-containers"
|
|
3
|
-
version = "4.
|
|
7
|
+
version = "4.1.0"
|
|
4
8
|
readme = "README.rst"
|
|
5
|
-
requires-python = ">=3.
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
requires-python = ">=3.13"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "Geoffrey R. Scheller", email = "geoffrey@scheller.com" },
|
|
12
|
+
]
|
|
8
13
|
keywords = [
|
|
9
14
|
"functional_containers",
|
|
10
15
|
"functional_tuple",
|
|
@@ -16,20 +21,20 @@ classifiers = [
|
|
|
16
21
|
"Intended Audience :: Developers",
|
|
17
22
|
"License :: OSI Approved :: Apache Software License",
|
|
18
23
|
"Operating System :: OS Independent",
|
|
19
|
-
"Programming Language :: Python :: 3.
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
20
25
|
"Typing :: Typed",
|
|
21
26
|
]
|
|
22
27
|
dependencies = [
|
|
23
|
-
"pythonic-fp-circulararray>=6.0.
|
|
24
|
-
"pythonic-fp-fptools>=5.
|
|
25
|
-
"pythonic-fp-iterables>=5.1.
|
|
28
|
+
"pythonic-fp-circulararray>=6.0.4",
|
|
29
|
+
"pythonic-fp-fptools>=5.2.0",
|
|
30
|
+
"pythonic-fp-iterables>=5.1.3",
|
|
26
31
|
]
|
|
27
32
|
dynamic = ["description"]
|
|
28
33
|
|
|
29
34
|
[project.urls]
|
|
30
35
|
Changelog = "https://github.com/grscheller/pythonic-fp-containers/blob/main/CHANGELOG.rst"
|
|
31
|
-
Documentation = "https://grscheller.github.io/pythonic-fp/containers
|
|
32
|
-
Homepage = "https://grscheller.github.io/pythonic-fp/
|
|
36
|
+
Documentation = "https://grscheller.github.io/pythonic-fp/projects/containers.html"
|
|
37
|
+
Homepage = "https://grscheller.github.io/pythonic-fp/"
|
|
33
38
|
Source = "https://github.com/grscheller/pythonic-fp-containers"
|
|
34
39
|
|
|
35
40
|
[project.optional-dependencies]
|
|
@@ -37,17 +42,11 @@ test = [
|
|
|
37
42
|
"pytest>=8.4.1",
|
|
38
43
|
]
|
|
39
44
|
|
|
40
|
-
[build-system]
|
|
41
|
-
requires = ["flit_core>=3.12,<4"]
|
|
42
|
-
build-backend = "flit_core.buildapi"
|
|
43
|
-
|
|
44
45
|
[tool.flit.module]
|
|
45
46
|
name = "pythonic_fp.containers"
|
|
46
47
|
|
|
47
48
|
[tool.mypy]
|
|
48
|
-
enable_incomplete_feature = "NewGenericSyntax"
|
|
49
49
|
explicit_package_bases = true
|
|
50
|
-
implicit_reexport = false
|
|
51
50
|
local_partial_types = true
|
|
52
51
|
namespace_packages = true
|
|
53
52
|
warn_redundant_casts = true
|
|
@@ -61,19 +60,24 @@ dmypy = false
|
|
|
61
60
|
strict = true
|
|
62
61
|
report_progress = true
|
|
63
62
|
|
|
64
|
-
[tool.pytest
|
|
65
|
-
addopts =
|
|
63
|
+
[tool.pytest]
|
|
64
|
+
addopts = [
|
|
65
|
+
"-ra",
|
|
66
|
+
]
|
|
66
67
|
consider_namespace_packages = true
|
|
67
68
|
testpaths = ["tests/"]
|
|
68
69
|
|
|
69
70
|
[tool.ruff]
|
|
70
|
-
target-version = "
|
|
71
|
+
target-version = "py314"
|
|
71
72
|
|
|
72
73
|
[tool.ruff.lint.flake8-quotes]
|
|
73
74
|
docstring-quotes = "double"
|
|
74
75
|
|
|
75
76
|
[tool.ruff.lint.per-file-ignores]
|
|
76
|
-
"tests
|
|
77
|
+
"tests/**/*.py" = [
|
|
78
|
+
"E731",
|
|
79
|
+
"C901",
|
|
80
|
+
]
|
|
77
81
|
"**/*.py" = ["E741"]
|
|
78
82
|
|
|
79
83
|
[tool.ruff.format]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Copyright 2023-2026 Geoffrey R. Scheller
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Containers with FP interfaces
|
|
17
|
+
-----------------------------
|
|
18
|
+
|
|
19
|
+
.. admonition:: Functional variants of Python builtins
|
|
20
|
+
|
|
21
|
+
Not sure the direction this should take.
|
|
22
|
+
|
|
23
|
+
- class **FTuple:** A tuple-like object with a functional interface.
|
|
24
|
+
- class **IList:** An immutable, hashable list-like object.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
__author__ = 'Geoffrey R. Scheller'
|
|
29
|
+
__copyright__ = 'Copyright (c) 2023-2026 Geoffrey R. Scheller'
|
|
30
|
+
__license__ = 'Apache License 2.0'
|
|
File without changes
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright 2023-
|
|
1
|
+
# Copyright 2023-2026 Geoffrey R. Scheller
|
|
2
|
+
|
|
2
3
|
#
|
|
3
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
5
|
# you may not use this file except in compliance with the License.
|
|
@@ -12,8 +13,6 @@
|
|
|
12
13
|
# See the License for the specific language governing permissions and
|
|
13
14
|
# limitations under the License.
|
|
14
15
|
|
|
15
|
-
"""Pythonic FP - Functional Tuple"""
|
|
16
|
-
|
|
17
16
|
from collections.abc import Callable, Iterator
|
|
18
17
|
from typing import cast, Never, overload, SupportsIndex
|
|
19
18
|
from pythonic_fp.iterables.folding import accumulate
|
|
@@ -23,28 +22,43 @@ __all__ = ['FTuple']
|
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
class FTuple[D](tuple[D, ...]):
|
|
26
|
-
"""
|
|
25
|
+
"""
|
|
26
|
+
.. admonition:: Functional Tuple
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
- intended for further inheritance
|
|
29
|
+
- supports both indexing and slicing
|
|
30
|
+
- int multiplication and FTuple addition supported
|
|
31
|
+
- addition concatenates results, resulting in a Union type
|
|
32
|
+
- both left and right int multiplication supported
|
|
33
|
+
- homogeneous in its data type
|
|
34
|
+
- unslotted
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- unslotted
|
|
36
|
+
.. note::
|
|
37
|
+
|
|
38
|
+
Example of code very early in project. Different ways
|
|
39
|
+
to use __dunders__ than what I do lately.
|
|
36
40
|
|
|
37
|
-
Since these tuples are homogeneous, their covariance may be quirky.
|
|
38
41
|
"""
|
|
39
42
|
|
|
40
43
|
def __reversed__(self) -> Iterator[D]:
|
|
44
|
+
"""
|
|
45
|
+
.. admonition:: reverse iter
|
|
46
|
+
|
|
47
|
+
:yields: the tuples elements in reverse order
|
|
48
|
+
|
|
49
|
+
"""
|
|
41
50
|
for ii in range(len(self) - 1, -1, -1):
|
|
42
51
|
yield (self[ii])
|
|
43
52
|
|
|
44
|
-
def __repr__(self) -> str:
|
|
45
|
-
return f'{self.__class__.__name__}(' + ', '.join(map(repr, self)) + ')'
|
|
46
|
-
|
|
47
53
|
def __eq__(self, other: object, /) -> bool:
|
|
54
|
+
"""
|
|
55
|
+
.. admonition:: equality comparison
|
|
56
|
+
|
|
57
|
+
:param other: The ``object`` to which to be compared.
|
|
58
|
+
:returns: ``True`` if ``other`` is the same type and
|
|
59
|
+
corresponding elements compare as equal.
|
|
60
|
+
|
|
61
|
+
"""
|
|
48
62
|
if not isinstance(other, self.__class__):
|
|
49
63
|
return False
|
|
50
64
|
if (length := len(self)) != len(other):
|
|
@@ -67,6 +81,9 @@ class FTuple[D](tuple[D, ...]):
|
|
|
67
81
|
else:
|
|
68
82
|
return super().__getitem__(idx)
|
|
69
83
|
|
|
84
|
+
def __repr__(self) -> str:
|
|
85
|
+
return f'{self.__class__.__name__}(' + ', '.join(map(repr, self)) + ')'
|
|
86
|
+
|
|
70
87
|
def foldl[L](
|
|
71
88
|
self,
|
|
72
89
|
f: Callable[[L, D], L],
|
|
@@ -74,16 +91,21 @@ class FTuple[D](tuple[D, ...]):
|
|
|
74
91
|
start: L | None = None,
|
|
75
92
|
default: L | None = None,
|
|
76
93
|
) -> L:
|
|
77
|
-
"""
|
|
94
|
+
"""
|
|
95
|
+
.. admonition:: fold left
|
|
96
|
+
|
|
97
|
+
Reduce left with optional starting and default values.
|
|
78
98
|
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
:param f: The folding function, first argument is for
|
|
100
|
+
the accumulated value.
|
|
101
|
+
:param start: An optional starting value.
|
|
102
|
+
:param default: An optional default value
|
|
103
|
+
if fold does not exist.
|
|
104
|
+
:raises ValueError: When ``FTuple`` empty and a start value
|
|
105
|
+
was not not given.
|
|
106
|
+
:returns: Folded value if it exists, otherwise
|
|
107
|
+
the default value if given.
|
|
81
108
|
|
|
82
|
-
:param f: The folding function, first argument is for the accumulated value.
|
|
83
|
-
:param start: An optional starting value.
|
|
84
|
-
:param default: An optional default value if fold does not exist.
|
|
85
|
-
:raises ValueError: When ``FTuple`` empty and a start value not given.
|
|
86
|
-
:returns: The folded value if it exists, otherwise the default value.
|
|
87
109
|
"""
|
|
88
110
|
it_self = iter(self)
|
|
89
111
|
if start is not None:
|
|
@@ -106,13 +128,21 @@ class FTuple[D](tuple[D, ...]):
|
|
|
106
128
|
start: R | None = None,
|
|
107
129
|
default: R | None = None,
|
|
108
130
|
) -> R:
|
|
109
|
-
"""
|
|
131
|
+
"""
|
|
132
|
+
.. admonition:: fold right
|
|
133
|
+
|
|
134
|
+
Reduce right with optional starting and default values.
|
|
135
|
+
|
|
136
|
+
:param f: The folding function, second argument is for
|
|
137
|
+
the accumulated value.
|
|
138
|
+
:param start: An optional starting value.
|
|
139
|
+
:param default: An optional default value
|
|
140
|
+
if fold does not exist.
|
|
141
|
+
:raises ValueError: when ``FTuple`` empty and a start value
|
|
142
|
+
was not given
|
|
143
|
+
:returns: The folded value if it exists, otherwise
|
|
144
|
+
the default value if given.
|
|
110
145
|
|
|
111
|
-
:param f: The folding function, second argument is for the accumulated value.
|
|
112
|
-
:param start: An optional starting value.
|
|
113
|
-
:param default: An optional default value if fold does not exist.
|
|
114
|
-
:raises ValueError: when FTuple empty and a start value not given
|
|
115
|
-
:returns: The folded value if it exists, otherwise the default value.
|
|
116
146
|
"""
|
|
117
147
|
it_self = reversed(self)
|
|
118
148
|
if start is not None:
|
|
@@ -129,9 +159,14 @@ class FTuple[D](tuple[D, ...]):
|
|
|
129
159
|
return acc
|
|
130
160
|
|
|
131
161
|
def copy(self) -> 'FTuple[D]':
|
|
132
|
-
"""
|
|
162
|
+
"""
|
|
163
|
+
.. admonition:: copy
|
|
164
|
+
|
|
165
|
+
Return a shallow copy of ``FTuple`` in O(1)
|
|
166
|
+
time & space complexity.
|
|
167
|
+
|
|
168
|
+
:returns: A new ``FTuple`` instance.
|
|
133
169
|
|
|
134
|
-
:returns: New ``FTuple``.
|
|
135
170
|
"""
|
|
136
171
|
return self.__class__(self)
|
|
137
172
|
|
|
@@ -150,20 +185,26 @@ class FTuple[D](tuple[D, ...]):
|
|
|
150
185
|
def accummulate[L](
|
|
151
186
|
self, f: Callable[[L, D], L], s: L | None = None, /
|
|
152
187
|
) -> 'FTuple[L]':
|
|
153
|
-
"""
|
|
188
|
+
"""
|
|
189
|
+
.. admonition:: accumulate partial folds
|
|
154
190
|
|
|
155
|
-
|
|
156
|
-
|
|
191
|
+
:param f: Folding function used to produce partial folds.
|
|
192
|
+
:param s: Optional starting value.
|
|
193
|
+
:returns: New ``FTuple`` of the partial folds.
|
|
157
194
|
|
|
158
|
-
:param f: Folding function used to produce partial folds.
|
|
159
|
-
:param s: Optional starting value.
|
|
160
|
-
:returns: New ``FTuple`` of the partial folds.
|
|
161
195
|
"""
|
|
162
196
|
if s is None:
|
|
163
197
|
return FTuple(accumulate(self, f))
|
|
164
198
|
return FTuple(accumulate(self, f, s))
|
|
165
199
|
|
|
166
200
|
def map[U](self, f: Callable[[D], U], /) -> 'FTuple[U]':
|
|
201
|
+
"""
|
|
202
|
+
.. admonition:: map
|
|
203
|
+
|
|
204
|
+
:param f: Folding function used to produce partial folds.
|
|
205
|
+
:returns: A new ``FTuple`` instance.
|
|
206
|
+
|
|
207
|
+
"""
|
|
167
208
|
return FTuple(map(f, self))
|
|
168
209
|
|
|
169
210
|
def bind[U](
|
|
@@ -172,13 +213,18 @@ class FTuple[D](tuple[D, ...]):
|
|
|
172
213
|
merge_type: MergeEnum = MergeEnum.Concat,
|
|
173
214
|
yield_partials: bool = False,
|
|
174
215
|
) -> 'FTuple[U] | Never':
|
|
175
|
-
"""
|
|
216
|
+
"""
|
|
217
|
+
.. admonition:: bind
|
|
218
|
+
|
|
219
|
+
Flat map function ``f`` with the ``FTuple`` with different
|
|
220
|
+
merging strategies.
|
|
221
|
+
|
|
222
|
+
:param f: Function ``D -> FTuple[U]``
|
|
223
|
+
:param merge_type: ``MergeEnum`` to determine how to merge the result.
|
|
224
|
+
:param yield_partials: Yield unmatched values if ``MergeEnum`` given as merge type.
|
|
225
|
+
:returns: A new ``FTuple`` instance.
|
|
226
|
+
:raises ValueError: If given an unknown merge enumeration.
|
|
176
227
|
|
|
177
|
-
:param f: Function ``D -> FTuple[U]``
|
|
178
|
-
:param merge_type: ``MergeEnum`` to determine how to merge the result.
|
|
179
|
-
:param yield_partials: Yield unmatched values if ``MergeEnum`` given as merge type.
|
|
180
|
-
:return: Resulting ``FTuple``.
|
|
181
|
-
:raises ValueError: If given an unknown merge enumeration.
|
|
182
228
|
"""
|
|
183
229
|
return FTuple(
|
|
184
230
|
blend(*map(f, self), merge_enum=merge_type, yield_partials=yield_partials)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2023-
|
|
1
|
+
# Copyright 2023-2026 Geoffrey R. Scheller
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -12,8 +12,6 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
"""Pythonic FP - Immutable guaranteed hashable lists."""
|
|
16
|
-
|
|
17
15
|
from collections.abc import Callable, Iterable, Iterator, Hashable
|
|
18
16
|
from typing import cast, Never, overload
|
|
19
17
|
from pythonic_fp.iterables.folding import accumulate
|
|
@@ -23,30 +21,34 @@ __all__ = ['IList']
|
|
|
23
21
|
|
|
24
22
|
|
|
25
23
|
class IList[D](Hashable):
|
|
26
|
-
"""
|
|
24
|
+
"""
|
|
25
|
+
.. admonition:: immutable list like data structure
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
- hashability should be enforced by LSP tooling
|
|
28
|
+
- hashability will be enforced at runtime
|
|
29
|
+
- its method type parameters are also covariant
|
|
30
|
+
- supports both indexing and slicing
|
|
31
|
+
- addition and left & right ``int`` multiplication supported
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
- addition is concatenation resulting in a union type
|
|
35
34
|
|
|
36
35
|
"""
|
|
37
36
|
|
|
38
37
|
__slots__ = ('_ds', '_len', '_hash')
|
|
39
38
|
__match_args__ = ('_ds', '_len')
|
|
40
39
|
|
|
41
|
-
def __init__(self, *
|
|
40
|
+
def __init__(self, *ds: Iterable[D]) -> None:
|
|
42
41
|
"""
|
|
43
|
-
|
|
42
|
+
.. admonition:: init
|
|
43
|
+
|
|
44
|
+
:param ds: One optional iterable.
|
|
45
|
+
|
|
44
46
|
"""
|
|
45
|
-
if (size := len(
|
|
47
|
+
if (size := len(ds)) > 1:
|
|
46
48
|
msg = f'IList expects at most 1 iterable argument, got {size}'
|
|
47
49
|
raise ValueError(msg)
|
|
48
50
|
else:
|
|
49
|
-
self._ds: tuple[D, ...] = tuple(
|
|
51
|
+
self._ds: tuple[D, ...] = tuple(ds[0]) if size == 1 else tuple()
|
|
50
52
|
self._len = len(self._ds)
|
|
51
53
|
try:
|
|
52
54
|
self._hash = hash((self._len, 42) + self._ds)
|
|
@@ -55,26 +57,55 @@ class IList[D](Hashable):
|
|
|
55
57
|
raise TypeError(msg)
|
|
56
58
|
|
|
57
59
|
def __hash__(self) -> int:
|
|
60
|
+
"""
|
|
61
|
+
.. admonition:: hash
|
|
62
|
+
|
|
63
|
+
Hashability enforced at runtime by initializer.
|
|
64
|
+
|
|
65
|
+
:returns: Hash value.
|
|
66
|
+
|
|
67
|
+
"""
|
|
58
68
|
return self._hash
|
|
59
69
|
|
|
60
70
|
def __iter__(self) -> Iterator[D]:
|
|
71
|
+
"""
|
|
72
|
+
.. admonition:: iterate
|
|
73
|
+
|
|
74
|
+
Forward iterate, left to right.
|
|
75
|
+
|
|
76
|
+
:yields: The contained values.
|
|
77
|
+
|
|
78
|
+
"""
|
|
61
79
|
return iter(self._ds)
|
|
62
80
|
|
|
63
81
|
def __reversed__(self) -> Iterator[D]:
|
|
82
|
+
"""
|
|
83
|
+
.. admonition:: iterate
|
|
84
|
+
|
|
85
|
+
Reverse iterate, left to right.
|
|
86
|
+
|
|
87
|
+
:yields: The contained values in reverse order.
|
|
88
|
+
|
|
89
|
+
"""
|
|
64
90
|
return reversed(self._ds)
|
|
65
91
|
|
|
66
92
|
def __bool__(self) -> bool:
|
|
93
|
+
"""
|
|
94
|
+
.. admonition:: bool
|
|
95
|
+
|
|
96
|
+
:returns: ``True`` if not empty, ``False`` if empty.
|
|
97
|
+
|
|
98
|
+
"""
|
|
67
99
|
return bool(self._ds)
|
|
68
100
|
|
|
69
101
|
def __len__(self) -> int:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def __repr__(self) -> str:
|
|
73
|
-
return 'immutable_list(' + ', '.join(map(repr, self)) + ')'
|
|
102
|
+
"""
|
|
103
|
+
.. admonition:: len
|
|
74
104
|
|
|
75
|
-
|
|
76
|
-
return '((' + ', '.join(map(repr, self)) + '))'
|
|
105
|
+
:returns: The number of items in the ``Ilist``.
|
|
77
106
|
|
|
107
|
+
"""
|
|
108
|
+
return len(self._ds)
|
|
78
109
|
def __eq__(self, other: object, /) -> bool:
|
|
79
110
|
if not isinstance(other, IList):
|
|
80
111
|
return NotImplemented # magic object
|
|
@@ -90,10 +121,37 @@ class IList[D](Hashable):
|
|
|
90
121
|
def __getitem__(self, idx: slice, /) -> 'IList[D]': ...
|
|
91
122
|
|
|
92
123
|
def __getitem__(self, idx: slice | int, /) -> 'IList[D] | D':
|
|
124
|
+
"""
|
|
125
|
+
.. admonition:: getitem
|
|
126
|
+
|
|
127
|
+
Indexable and sliceable but otherwise immutable.
|
|
128
|
+
|
|
129
|
+
:param idx: Either a ``slice`` or and ``index``.
|
|
130
|
+
:returns: A slice or the value.
|
|
131
|
+
|
|
132
|
+
"""
|
|
93
133
|
if isinstance(idx, slice):
|
|
94
134
|
return IList(self._ds[idx])
|
|
95
135
|
return self._ds[idx]
|
|
96
136
|
|
|
137
|
+
def __repr__(self) -> str:
|
|
138
|
+
"""
|
|
139
|
+
.. admonition:: repr string
|
|
140
|
+
|
|
141
|
+
:returns: A string to reproduce the value.
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
return 'immutable_list(' + ', '.join(map(repr, self)) + ')'
|
|
145
|
+
|
|
146
|
+
def __str__(self) -> str:
|
|
147
|
+
"""
|
|
148
|
+
.. admonition:: user string
|
|
149
|
+
|
|
150
|
+
:returns: A string meaningful to an end user.
|
|
151
|
+
|
|
152
|
+
"""
|
|
153
|
+
return '((' + ', '.join(map(repr, self)) + '))'
|
|
154
|
+
|
|
97
155
|
def foldl[L](
|
|
98
156
|
self,
|
|
99
157
|
f: Callable[[L, D], L],
|
|
@@ -101,13 +159,17 @@ class IList[D](Hashable):
|
|
|
101
159
|
start: L | None = None,
|
|
102
160
|
default: L | None = None,
|
|
103
161
|
) -> L | None:
|
|
104
|
-
"""
|
|
162
|
+
"""
|
|
163
|
+
.. admonition:: fold left
|
|
164
|
+
|
|
165
|
+
:param f: Folding function, first argument is for
|
|
166
|
+
the accumulated value.
|
|
167
|
+
:param start: Optional starting value.
|
|
168
|
+
:param default: Optional default value if fold does not exist.
|
|
169
|
+
:returns: Left folded value.
|
|
170
|
+
:raises ValueError: When empty and neither a start value or
|
|
171
|
+
default value not given.
|
|
105
172
|
|
|
106
|
-
:param f: Folding function, first argument is for the accumulated value.
|
|
107
|
-
:param start: Optional starting value.
|
|
108
|
-
:param default: Optional default value if fold does not exist.
|
|
109
|
-
:returns: Folded value.
|
|
110
|
-
:raises ValueError: When empty and a start value not given.
|
|
111
173
|
"""
|
|
112
174
|
it = iter(self._ds)
|
|
113
175
|
if start is not None:
|
|
@@ -132,13 +194,17 @@ class IList[D](Hashable):
|
|
|
132
194
|
start: R | None = None,
|
|
133
195
|
default: R | None = None,
|
|
134
196
|
) -> R | None:
|
|
135
|
-
"""
|
|
197
|
+
"""
|
|
198
|
+
.. admonition:: fold right
|
|
199
|
+
|
|
200
|
+
:param f: Folding function, second argument is for
|
|
201
|
+
the accumulated value.
|
|
202
|
+
:param start: Optional starting value.
|
|
203
|
+
:param default: Optional default value if fold does not exist.
|
|
204
|
+
:returns: Right folded value.
|
|
205
|
+
:raises ValueError: When empty and neither a start value or
|
|
206
|
+
default value not given.
|
|
136
207
|
|
|
137
|
-
:param f: Folding function, second argument is for the accumulated value.
|
|
138
|
-
:param start: Optional starting value.
|
|
139
|
-
:param default: Optional default value if fold does not exist.
|
|
140
|
-
:returns: Folded value.
|
|
141
|
-
:raises ValueError: When empty and a start value not given.
|
|
142
208
|
"""
|
|
143
209
|
it = reversed(self._ds)
|
|
144
210
|
if start is not None:
|
|
@@ -157,6 +223,18 @@ class IList[D](Hashable):
|
|
|
157
223
|
return acc
|
|
158
224
|
|
|
159
225
|
def __add__(self, other: 'IList[D]', /) -> 'IList[D]':
|
|
226
|
+
"""
|
|
227
|
+
.. admonition:: add by concatenation
|
|
228
|
+
|
|
229
|
+
:param other: The ``other`` same typed ``IList``.
|
|
230
|
+
:returns: New ``IList`` instance with concatenated values.
|
|
231
|
+
:raises ValueError: When ``other`` is not an ``IList``.
|
|
232
|
+
|
|
233
|
+
.. admonition:: TODO
|
|
234
|
+
|
|
235
|
+
Return ``NotImplemented`` if appropriate?
|
|
236
|
+
|
|
237
|
+
"""
|
|
160
238
|
if not isinstance(other, IList):
|
|
161
239
|
msg = 'IList being added to something not an IList'
|
|
162
240
|
raise ValueError(msg)
|
|
@@ -164,28 +242,59 @@ class IList[D](Hashable):
|
|
|
164
242
|
return IList(concat(self, other))
|
|
165
243
|
|
|
166
244
|
def __mul__(self, num: int, /) -> 'IList[D]':
|
|
245
|
+
"""
|
|
246
|
+
.. admonition:: add by concatenation
|
|
247
|
+
|
|
248
|
+
:param other: The ``other`` same typed ``IList``.
|
|
249
|
+
:returns: New ``IList`` instance with concatenated values.
|
|
250
|
+
:raises ValueError: When ``other`` is not an ``IList``.
|
|
251
|
+
|
|
252
|
+
.. admonition:: TODO
|
|
253
|
+
|
|
254
|
+
Consider returning ``NotImplemented`` if appropriate?
|
|
255
|
+
Maybe define a "column vector" version?
|
|
256
|
+
|
|
257
|
+
"""
|
|
167
258
|
return IList(self._ds.__mul__(num if num > 0 else 0))
|
|
168
259
|
|
|
169
260
|
def __rmul__(self, num: int, /) -> 'IList[D]':
|
|
261
|
+
"""
|
|
262
|
+
.. admonition:: concatenation with itself
|
|
263
|
+
|
|
264
|
+
:param num: Number of times to concatenate ``IList``
|
|
265
|
+
with itself.
|
|
266
|
+
:returns: A new ``IList`` instance with concatenated values.
|
|
267
|
+
:raises ValueError: When ``other`` is not an ``IList``.
|
|
268
|
+
|
|
269
|
+
"""
|
|
170
270
|
return IList(self._ds.__mul__(num if num > 0 else 0))
|
|
171
271
|
|
|
172
272
|
def accummulate[L](
|
|
173
273
|
self, f: Callable[[L, D], L], s: L | None = None, /
|
|
174
274
|
) -> 'IList[L]':
|
|
175
|
-
"""
|
|
275
|
+
"""
|
|
276
|
+
.. admonition:: accumulate partial folds
|
|
277
|
+
|
|
278
|
+
Accumulate partial fold with an optional starting value,
|
|
279
|
+
results in an ``IList``.
|
|
176
280
|
|
|
177
|
-
|
|
178
|
-
|
|
281
|
+
:param f: Folding function used to produce partial folds.
|
|
282
|
+
:param s: Optional starting value.
|
|
283
|
+
:returns: New ``IList`` of the partial folds.
|
|
179
284
|
|
|
180
|
-
:param f: Folding function used to produce partial folds.
|
|
181
|
-
:param s: Optional starting value.
|
|
182
|
-
:returns: New ``FTuple`` of the partial folds.
|
|
183
285
|
"""
|
|
184
286
|
if s is None:
|
|
185
287
|
return IList(accumulate(self, f))
|
|
186
288
|
return IList(accumulate(self, f, s))
|
|
187
289
|
|
|
188
290
|
def map[U](self, f: Callable[[D], U], /) -> 'IList[U]':
|
|
291
|
+
"""
|
|
292
|
+
.. admonition:: map
|
|
293
|
+
|
|
294
|
+
:parameter f: Function to map over the ``IList``.
|
|
295
|
+
:returns: A new ``IList`` instance with the mapped values.
|
|
296
|
+
|
|
297
|
+
"""
|
|
189
298
|
return IList(map(f, self))
|
|
190
299
|
|
|
191
300
|
def bind[U](
|
|
@@ -194,20 +303,18 @@ class IList[D](Hashable):
|
|
|
194
303
|
merge_enum: MergeEnum = MergeEnum.Concat,
|
|
195
304
|
yield_partials: bool = False,
|
|
196
305
|
) -> 'IList[U] | Never':
|
|
197
|
-
"""Bind function `f` to the `IList`.
|
|
198
|
-
|
|
199
|
-
:param ds: values to instantiate IList
|
|
200
|
-
:return: resulting IList
|
|
201
|
-
:raises ValueError: if given unknown merge_type
|
|
202
|
-
|
|
203
306
|
"""
|
|
204
|
-
|
|
307
|
+
.. admonition:: bind
|
|
308
|
+
|
|
309
|
+
:param f: Function ``D -> IList[U]`` to bind with different
|
|
310
|
+
merge strategies.
|
|
311
|
+
:param merge_type: ``MergeEnum`` to determine how to merge
|
|
312
|
+
the result.
|
|
313
|
+
:param yield_partials: Yield unmatched values if ``MergeEnum``
|
|
314
|
+
given as merge type.
|
|
315
|
+
:return: New ``IList`` instance.
|
|
316
|
+
:raises ValueError: If given an unknown merge enumeration.
|
|
205
317
|
|
|
206
|
-
:param f: Function ``D -> IList[U]``
|
|
207
|
-
:param merge_type: ``MergeEnum`` to determine how to merge the result.
|
|
208
|
-
:param yield_partials: Yield unmatched values if ``MergeEnum`` given as merge type.
|
|
209
|
-
:return: Resulting ``IList``.
|
|
210
|
-
:raises ValueError: If given an unknown merge enumeration.
|
|
211
318
|
"""
|
|
212
319
|
return IList(
|
|
213
320
|
blend(*map(f, self), merge_enum=merge_enum, yield_partials=yield_partials)
|
|
@@ -7,7 +7,7 @@ __all__ = ['IList']
|
|
|
7
7
|
|
|
8
8
|
class IList[D](Hashable):
|
|
9
9
|
__match_args__: Incomplete
|
|
10
|
-
def __init__(self, *
|
|
10
|
+
def __init__(self, *ds: Iterable[D]) -> None: ...
|
|
11
11
|
def __hash__(self) -> int: ...
|
|
12
12
|
def __iter__(self) -> Iterator[D]: ...
|
|
13
13
|
def __reversed__(self) -> Iterator[D]: ...
|
|
File without changes
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# Copyright 2023-2025 Geoffrey R. Scheller
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
"""
|
|
16
|
-
Container with FP interfaces
|
|
17
|
-
============================
|
|
18
|
-
|
|
19
|
-
+---------------------------------------------+-----------+------------------+
|
|
20
|
-
| module | class | description |
|
|
21
|
-
+=============================================+===========+==================+
|
|
22
|
-
| ``pythonic_fp.containers.functional_tuple`` | FTuple | Functional Tuple |
|
|
23
|
-
+---------------------------------------------+-----------+------------------+
|
|
24
|
-
| ``pythonic_fp.containers.immutable_list`` | IList | Immutable List |
|
|
25
|
-
+---------------------------------------------+-----------+------------------+
|
|
26
|
-
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
__author__ = 'Geoffrey R. Scheller'
|
|
30
|
-
__copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
|
|
31
|
-
__license__ = 'Apache License 2.0'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/tests/test_functional_tuple.py
RENAMED
|
File without changes
|
|
File without changes
|
{pythonic_fp_containers-4.0.0 → pythonic_fp_containers-4.1.0}/tests/test_immutable_list_fp.py
RENAMED
|
File without changes
|