pythonic-fp.splitends 2.0.0__tar.gz → 2.0.3__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_splitends-2.0.3/.github/workflows/static.yml +64 -0
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/.gitignore +2 -0
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/CHANGELOG.rst +16 -0
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/PKG-INFO +17 -12
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/README.rst +9 -4
- pythonic_fp_splitends-2.0.3/docs/Makefile +78 -0
- pythonic_fp_splitends-2.0.3/docs/gen_conf.py +110 -0
- pythonic_fp_splitends-2.0.3/docs/requirements.txt +2 -0
- pythonic_fp_splitends-2.0.3/docs/source/_static/custom.css +3 -0
- pythonic_fp_splitends-2.0.3/docs/source/api/index.rst +15 -0
- pythonic_fp_splitends-2.0.3/docs/source/api/splitend.rst +8 -0
- pythonic_fp_splitends-2.0.3/docs/source/api/splitend_node.rst +8 -0
- pythonic_fp_splitends-2.0.3/docs/source/changelog.rst +7 -0
- pythonic_fp_splitends-2.0.3/docs/source/description.rst +7 -0
- pythonic_fp_splitends-2.0.3/docs/source/index.rst +30 -0
- pythonic_fp_splitends-2.0.3/docs/source/releases.rst +24 -0
- pythonic_fp_splitends-2.0.3/docs/source/usage.rst +21 -0
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/pyproject.toml +28 -20
- pythonic_fp_splitends-2.0.3/src/pythonic_fp/splitends/__init__.py +41 -0
- pythonic_fp_splitends-2.0.3/src/pythonic_fp/splitends/__init__.pyi +0 -0
- pythonic_fp_splitends-2.0.3/src/pythonic_fp/splitends/py.typed +0 -0
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/src/pythonic_fp/splitends/splitend.py +168 -60
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/src/pythonic_fp/splitends/splitend_node.py +84 -38
- pythonic_fp_splitends-2.0.0/src/pythonic_fp/splitends/__init__.py +0 -37
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/LICENSE +0 -0
- /pythonic_fp_splitends-2.0.0/src/pythonic_fp/splitends/__init__.pyi → /pythonic_fp_splitends-2.0.3/docs/source/_static/.gitkeep +0 -0
- /pythonic_fp_splitends-2.0.0/src/pythonic_fp/splitends/py.typed → /pythonic_fp_splitends-2.0.3/docs/source/_templates/.gitkeep +0 -0
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/src/pythonic_fp/splitends/splitend.pyi +0 -0
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/src/pythonic_fp/splitends/splitend_node.pyi +0 -0
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/tests/test_splitend.py +0 -0
- {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/tests/test_splitend_node.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: '2.0.3'
|
|
10
|
+
DEVEL: '2.0.4'
|
|
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: '3.14'
|
|
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-splitends
|
|
45
|
+
docs/gen_conf.py release ${{ env.RELEASE }} Splitends splitends > 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 }} Splitends splitends > 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,22 @@ See `Semantic Versioning 2.0.0 <https://semver.org>`_.
|
|
|
17
17
|
Releases and Important Milestones
|
|
18
18
|
---------------------------------
|
|
19
19
|
|
|
20
|
+
PyPI 2.0.3 - 2026-05-13
|
|
21
|
+
~~~~~~~~~~~~~~~~~~~~~~~
|
|
22
|
+
|
|
23
|
+
- Documentation now in maintenance mode.
|
|
24
|
+
|
|
25
|
+
Development Status Reappraisal - 2026-05-05
|
|
26
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
27
|
+
|
|
28
|
+
Maintainer appraised the Development Status for
|
|
29
|
+
pythonic-fp-splitends to be ``"3 - Alpha"``.
|
|
30
|
+
|
|
31
|
+
PyPI 2.0.2 - 2026-01-22
|
|
32
|
+
~~~~~~~~~~~~~~~~~~~~~~~
|
|
33
|
+
|
|
34
|
+
- Docstring improvements.
|
|
35
|
+
|
|
20
36
|
PyPI 2.0.0 - 2025-09-09
|
|
21
37
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
22
38
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonic-fp-splitends
|
|
3
|
-
Version: 2.0.
|
|
4
|
-
Summary: Mutable stack objects that can safely share data
|
|
3
|
+
Version: 2.0.3
|
|
4
|
+
Summary: .. admonition:: Mutable stack objects that can safely share data
|
|
5
5
|
Keywords: SplitEnd,lifo,stack
|
|
6
6
|
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
|
7
7
|
Requires-Python: >=3.13
|
|
@@ -11,16 +11,16 @@ 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-fptools>=5.
|
|
18
|
-
Requires-Dist: pythonic-fp-iterables>=5.1.
|
|
19
|
-
Requires-Dist: pythonic-fp-queues>=5.1.
|
|
17
|
+
Requires-Dist: pythonic-fp-fptools>=5.2.0
|
|
18
|
+
Requires-Dist: pythonic-fp-iterables>=5.1.3
|
|
19
|
+
Requires-Dist: pythonic-fp-queues>=5.1.1
|
|
20
20
|
Requires-Dist: pytest>=8.4.1 ; extra == "test"
|
|
21
21
|
Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-splitends/blob/main/CHANGELOG.rst
|
|
22
|
-
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/splitends
|
|
23
|
-
Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/
|
|
22
|
+
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/projects/splitends.html
|
|
23
|
+
Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/
|
|
24
24
|
Project-URL: Source, https://github.com/grscheller/pythonic-fp-splitends
|
|
25
25
|
Provides-Extra: test
|
|
26
26
|
|
|
@@ -28,7 +28,8 @@ Pythonic FP - SplitEnds
|
|
|
28
28
|
=======================
|
|
29
29
|
|
|
30
30
|
PyPI project
|
|
31
|
-
`pythonic-fp.splitends
|
|
31
|
+
`pythonic-fp.splitends
|
|
32
|
+
<https://pypi.org/project/pythonic-fp-splitends/>`_.
|
|
32
33
|
|
|
33
34
|
Python package Implementing a singularly linked LIFO queue called
|
|
34
35
|
a ``SplitEnd``. These data structures can safely share data nodes
|
|
@@ -45,19 +46,23 @@ between themselves.
|
|
|
45
46
|
|
|
46
47
|
Part of the
|
|
47
48
|
`pythonic-fp
|
|
48
|
-
<https://grscheller.github.io/pythonic-fp
|
|
49
|
+
<https://grscheller.github.io/pythonic-fp/>`_
|
|
49
50
|
PyPI projects.
|
|
50
51
|
|
|
51
52
|
Documentation
|
|
52
53
|
-------------
|
|
53
54
|
|
|
55
|
+
Documentation and other links for this project are hosted on
|
|
56
|
+
`GitHub Pages
|
|
57
|
+
<https://grscheller.github.io/pythonic-fp/projects/splitends.html>`_.
|
|
58
|
+
|
|
54
59
|
Documentation for this project is hosted on
|
|
55
60
|
`GitHub Pages
|
|
56
|
-
<https://grscheller.github.io/pythonic-fp
|
|
61
|
+
<https://grscheller.github.io/pythonic-fp-splitends/development/html/>`_.
|
|
57
62
|
|
|
58
63
|
Copyright and License
|
|
59
64
|
---------------------
|
|
60
65
|
|
|
61
|
-
Copyright (c) 2023-
|
|
66
|
+
Copyright (c) 2023-2026 Geoffrey R. Scheller. Licensed under the Apache
|
|
62
67
|
License, Version 2.0. See the LICENSE file for details.
|
|
63
68
|
|
|
@@ -2,7 +2,8 @@ Pythonic FP - SplitEnds
|
|
|
2
2
|
=======================
|
|
3
3
|
|
|
4
4
|
PyPI project
|
|
5
|
-
`pythonic-fp.splitends
|
|
5
|
+
`pythonic-fp.splitends
|
|
6
|
+
<https://pypi.org/project/pythonic-fp-splitends/>`_.
|
|
6
7
|
|
|
7
8
|
Python package Implementing a singularly linked LIFO queue called
|
|
8
9
|
a ``SplitEnd``. These data structures can safely share data nodes
|
|
@@ -19,18 +20,22 @@ between themselves.
|
|
|
19
20
|
|
|
20
21
|
Part of the
|
|
21
22
|
`pythonic-fp
|
|
22
|
-
<https://grscheller.github.io/pythonic-fp
|
|
23
|
+
<https://grscheller.github.io/pythonic-fp/>`_
|
|
23
24
|
PyPI projects.
|
|
24
25
|
|
|
25
26
|
Documentation
|
|
26
27
|
-------------
|
|
27
28
|
|
|
29
|
+
Documentation and other links for this project are hosted on
|
|
30
|
+
`GitHub Pages
|
|
31
|
+
<https://grscheller.github.io/pythonic-fp/projects/splitends.html>`_.
|
|
32
|
+
|
|
28
33
|
Documentation for this project is hosted on
|
|
29
34
|
`GitHub Pages
|
|
30
|
-
<https://grscheller.github.io/pythonic-fp
|
|
35
|
+
<https://grscheller.github.io/pythonic-fp-splitends/development/html/>`_.
|
|
31
36
|
|
|
32
37
|
Copyright and License
|
|
33
38
|
---------------------
|
|
34
39
|
|
|
35
|
-
Copyright (c) 2023-
|
|
40
|
+
Copyright (c) 2023-2026 Geoffrey R. Scheller. Licensed under the Apache
|
|
36
41
|
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 = Splitends
|
|
7
|
+
PYPI_NAME = splitends
|
|
8
|
+
RELEASE_VERSION = 2.0.3
|
|
9
|
+
DEVEL_VERSION = 2.0.4
|
|
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,30 @@
|
|
|
1
|
+
pythonic-fp-splitends
|
|
2
|
+
=====================
|
|
3
|
+
|
|
4
|
+
Project
|
|
5
|
+
`Pypythonic-fp-Splitends <https://pypi.org/project/pythonic-fp-splitends/>`_
|
|
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,24 @@
|
|
|
1
|
+
PyPI releases
|
|
2
|
+
=============
|
|
3
|
+
|
|
4
|
+
**pythonic-fp-splitends:** pythonic_fp.splitends
|
|
5
|
+
|
|
6
|
+
+---------------------------------------------------------------------------------------+--------------+
|
|
7
|
+
| PyPI Release | Release date |
|
|
8
|
+
+=======================================================================================+==============+
|
|
9
|
+
| `development <https://grscheller.github.io/pythonic-fp-splitends/development/html/>`_ | |
|
|
10
|
+
+---------------------------------------------------------------------------------------+--------------+
|
|
11
|
+
| `v2.0.3 <https://grscheller.github.io/pythonic-fp-splitends/release/html/>`_ | 2026-05-13 |
|
|
12
|
+
+---------------------------------------------------------------------------------------+--------------+
|
|
13
|
+
| v2.0.2 | 2026-01-22 |
|
|
14
|
+
+---------------------------------------------------------------------------------------+--------------+
|
|
15
|
+
| v2.0.0 | 2025-09-28 |
|
|
16
|
+
+---------------------------------------------------------------------------------------+--------------+
|
|
17
|
+
| v1.0.2 | 2025-09-09 |
|
|
18
|
+
+---------------------------------------------------------------------------------------+--------------+
|
|
19
|
+
| v1.0.1 | 2025-09-09 |
|
|
20
|
+
+---------------------------------------------------------------------------------------+--------------+
|
|
21
|
+
| v1.0.0 | 2025-08-02 |
|
|
22
|
+
+---------------------------------------------------------------------------------------+--------------+
|
|
23
|
+
| v0.30.0 | 2025-07-06 |
|
|
24
|
+
+---------------------------------------------------------------------------------------+--------------+
|
|
@@ -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.splitends
|
|
12
|
+
|
|
13
|
+
Importing the package
|
|
14
|
+
---------------------
|
|
15
|
+
|
|
16
|
+
Import the Splitend class and its supporting infrastructure (TODO)
|
|
17
|
+
into your code.
|
|
18
|
+
|
|
19
|
+
.. code:: python
|
|
20
|
+
|
|
21
|
+
from pythonic_fp.splitends.splitend import Splitend
|
|
@@ -1,31 +1,40 @@
|
|
|
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-splitends"
|
|
3
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.3"
|
|
4
8
|
readme = "README.rst"
|
|
5
9
|
requires-python = ">=3.13"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "Geoffrey R. Scheller", email = "geoffrey@scheller.com" },
|
|
12
|
+
]
|
|
13
|
+
keywords = [
|
|
14
|
+
"SplitEnd",
|
|
15
|
+
"lifo",
|
|
16
|
+
"stack",
|
|
17
|
+
]
|
|
9
18
|
classifiers = [
|
|
10
19
|
"Development Status :: 3 - Alpha",
|
|
11
20
|
"Framework :: Pytest",
|
|
12
21
|
"Intended Audience :: Developers",
|
|
13
22
|
"License :: OSI Approved :: Apache Software License",
|
|
14
23
|
"Operating System :: OS Independent",
|
|
15
|
-
"Programming Language :: Python :: 3.
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
16
25
|
"Typing :: Typed",
|
|
17
26
|
]
|
|
18
27
|
dependencies = [
|
|
19
|
-
"pythonic-fp-fptools>=5.
|
|
20
|
-
"pythonic-fp-iterables>=5.1.
|
|
21
|
-
"pythonic-fp-queues>=5.1.
|
|
28
|
+
"pythonic-fp-fptools>=5.2.0",
|
|
29
|
+
"pythonic-fp-iterables>=5.1.3",
|
|
30
|
+
"pythonic-fp-queues>=5.1.1",
|
|
22
31
|
]
|
|
23
32
|
dynamic = ["description"]
|
|
24
33
|
|
|
25
34
|
[project.urls]
|
|
26
35
|
Changelog = "https://github.com/grscheller/pythonic-fp-splitends/blob/main/CHANGELOG.rst"
|
|
27
|
-
Documentation = "https://grscheller.github.io/pythonic-fp/splitends
|
|
28
|
-
Homepage = "https://grscheller.github.io/pythonic-fp/
|
|
36
|
+
Documentation = "https://grscheller.github.io/pythonic-fp/projects/splitends.html"
|
|
37
|
+
Homepage = "https://grscheller.github.io/pythonic-fp/"
|
|
29
38
|
Source = "https://github.com/grscheller/pythonic-fp-splitends"
|
|
30
39
|
|
|
31
40
|
[project.optional-dependencies]
|
|
@@ -33,17 +42,11 @@ test = [
|
|
|
33
42
|
"pytest>=8.4.1",
|
|
34
43
|
]
|
|
35
44
|
|
|
36
|
-
[build-system]
|
|
37
|
-
requires = ["flit_core>=3.12,<4"]
|
|
38
|
-
build-backend = "flit_core.buildapi"
|
|
39
|
-
|
|
40
45
|
[tool.flit.module]
|
|
41
46
|
name = "pythonic_fp.splitends"
|
|
42
47
|
|
|
43
48
|
[tool.mypy]
|
|
44
|
-
enable_incomplete_feature = "NewGenericSyntax"
|
|
45
49
|
explicit_package_bases = true
|
|
46
|
-
implicit_reexport = false
|
|
47
50
|
local_partial_types = true
|
|
48
51
|
namespace_packages = true
|
|
49
52
|
warn_redundant_casts = true
|
|
@@ -57,19 +60,24 @@ dmypy = false
|
|
|
57
60
|
strict = true
|
|
58
61
|
report_progress = true
|
|
59
62
|
|
|
60
|
-
[tool.pytest
|
|
61
|
-
addopts =
|
|
63
|
+
[tool.pytest]
|
|
64
|
+
addopts = [
|
|
65
|
+
"-ra",
|
|
66
|
+
]
|
|
62
67
|
consider_namespace_packages = true
|
|
63
68
|
testpaths = ["tests/"]
|
|
64
69
|
|
|
65
70
|
[tool.ruff]
|
|
66
|
-
target-version = "
|
|
71
|
+
target-version = "py314"
|
|
67
72
|
|
|
68
73
|
[tool.ruff.lint.flake8-quotes]
|
|
69
74
|
docstring-quotes = "double"
|
|
70
75
|
|
|
71
76
|
[tool.ruff.lint.per-file-ignores]
|
|
72
|
-
"tests
|
|
77
|
+
"tests/**/*.py" = [
|
|
78
|
+
"E731",
|
|
79
|
+
"C901",
|
|
80
|
+
]
|
|
73
81
|
"**/*.py" = ["E741"]
|
|
74
82
|
|
|
75
83
|
[tool.ruff.format]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Copyright 2024-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
|
+
.. admonition:: Mutable stack objects that can safely share data
|
|
17
|
+
|
|
18
|
+
Python package implementing a singularly linked stack of nodes called a ``SplitEnd``.
|
|
19
|
+
While mutable, these data structures can safely share immutable data nodes between
|
|
20
|
+
themselves. These nodes, of type ``SENode``, always contain data. A root node is
|
|
21
|
+
a node whose previous node is itself. Root nodes mark the bottom of the stack.
|
|
22
|
+
|
|
23
|
+
Like one of many "split ends" from shafts of hair, a ``SplitEnd`` can be "snipped"
|
|
24
|
+
shorter or "extended" further from its "end" node at the top of the stack. Its "root"
|
|
25
|
+
node is at the bottom of the stack and can not be "snipped" or "cut" off.
|
|
26
|
+
|
|
27
|
+
A ``SplitEnd`` can be duplicated onto a compatible root node. By compatible, the
|
|
28
|
+
new root must contain data that compares as equal to the old root node.
|
|
29
|
+
|
|
30
|
+
.. admonition:: Future directions
|
|
31
|
+
|
|
32
|
+
A ``scalp`` is a container of root nodes all of whose contained data must
|
|
33
|
+
be hashable. The ``scalp.add(d)`` method will return the unique root node with data
|
|
34
|
+
that compares as equal to ``d``, or create a new root node if such a contained node
|
|
35
|
+
does not exist.
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
__author__ = 'Geoffrey R. Scheller'
|
|
40
|
+
__copyright__ = 'Copyright (c) 2023-2026 Geoffrey R. Scheller'
|
|
41
|
+
__license__ = 'Apache License 2.0'
|
|
File without changes
|
|
File without changes
|
{pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/src/pythonic_fp/splitends/splitend.py
RENAMED
|
@@ -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.
|
|
@@ -14,27 +14,33 @@
|
|
|
14
14
|
|
|
15
15
|
"""
|
|
16
16
|
Class SplitEnds
|
|
17
|
-
|
|
17
|
+
---------------
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
.. admonition:: LIFO stacks safely sharing immutable data.
|
|
20
20
|
|
|
21
|
-
- each ``SplitEnd`` is a very simple stateful (mutable) LIFO stack
|
|
22
|
-
- data can be either "extended" to or "snipped" off the "end"
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
21
|
+
- each ``SplitEnd`` is a very simple stateful (mutable) LIFO stack
|
|
22
|
+
- data can be either "extended" to or "snipped" off the "end"
|
|
23
|
+
- "root" of a ``SplitEnd`` is embedded in a ``scalp``
|
|
24
|
+
|
|
25
|
+
- the root data is immutable
|
|
26
|
+
- it cannot be split off
|
|
27
|
+
|
|
28
|
+
- different mutable split ends can safely share the same "tail"
|
|
29
|
+
- each ``SplitEnd`` sees itself as a singularly linked list
|
|
30
|
+
- bush-like datastructures can be formed using multiple ``SplitEnds``
|
|
31
|
+
- the ``SplitEnd.split`` and ``len`` methods are O(1)
|
|
32
|
+
- in a boolean context
|
|
33
|
+
|
|
34
|
+
- falsy just a "root"
|
|
35
|
+
- truthy otherwise
|
|
30
36
|
|
|
31
37
|
"""
|
|
32
38
|
|
|
33
39
|
from collections.abc import Callable, Iterator
|
|
34
|
-
from pythonic_fp.iterables.folding import reduce_left, fold_left
|
|
35
40
|
from typing import cast, Final, overload
|
|
36
|
-
from pythonic_fp.queues.lifo import LIFOQueue
|
|
37
41
|
from pythonic_fp.gadgets.sentinels.flavored import Sentinel
|
|
42
|
+
from pythonic_fp.iterables.folding import reduce_left, fold_left
|
|
43
|
+
from pythonic_fp.queues.lifo import LIFOQueue
|
|
38
44
|
from .splitend_node import SENode
|
|
39
45
|
|
|
40
46
|
__all__ = ['SplitEnd']
|
|
@@ -50,8 +56,11 @@ class SplitEnd[D]:
|
|
|
50
56
|
|
|
51
57
|
def __init__(self, *ds: D, root: SENode[D] | _Sentinel = _sentinel) -> None:
|
|
52
58
|
"""
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
.. admonition:: init
|
|
60
|
+
|
|
61
|
+
:param root_data: Irremovable initial data at bottom of stack.
|
|
62
|
+
:param ds: Removable data to be pushed onto splitend stack.
|
|
63
|
+
|
|
55
64
|
"""
|
|
56
65
|
if root is _sentinel:
|
|
57
66
|
if ds:
|
|
@@ -75,27 +84,57 @@ class SplitEnd[D]:
|
|
|
75
84
|
self._end, self._root, self._count = end, root_node, count
|
|
76
85
|
|
|
77
86
|
def __iter__(self) -> Iterator[D]:
|
|
87
|
+
"""
|
|
88
|
+
.. admonition:: iter
|
|
89
|
+
|
|
90
|
+
Iterate from end to root.
|
|
91
|
+
|
|
92
|
+
:yields: data from end to root
|
|
93
|
+
|
|
94
|
+
"""
|
|
78
95
|
return iter(self._end)
|
|
79
96
|
|
|
80
97
|
def __reversed__(self) -> Iterator[D]:
|
|
98
|
+
"""
|
|
99
|
+
.. admonition:: reverse iter
|
|
100
|
+
|
|
101
|
+
Iterate from end to root.
|
|
102
|
+
|
|
103
|
+
:yields: data from end to root
|
|
104
|
+
|
|
105
|
+
"""
|
|
81
106
|
return reversed(list(self))
|
|
82
107
|
|
|
83
108
|
def __bool__(self) -> bool:
|
|
84
109
|
"""
|
|
85
|
-
|
|
110
|
+
.. admonition:: bool
|
|
111
|
+
|
|
112
|
+
:returns: ``True`` if ``SplitEnd`` is not just its root node.
|
|
113
|
+
|
|
86
114
|
"""
|
|
87
115
|
return bool(self._end)
|
|
88
116
|
|
|
89
117
|
def __len__(self) -> int:
|
|
90
|
-
|
|
118
|
+
"""
|
|
119
|
+
.. admonition:: len
|
|
91
120
|
|
|
92
|
-
|
|
93
|
-
return 'SplitEend(' + ', '.join(map(repr, reversed(self))) + ')'
|
|
121
|
+
:returns: The number of nodes on the ``Splitend``.
|
|
94
122
|
|
|
95
|
-
|
|
96
|
-
return
|
|
123
|
+
"""
|
|
124
|
+
return self._count
|
|
97
125
|
|
|
98
126
|
def __eq__(self, other: object, /) -> bool:
|
|
127
|
+
"""
|
|
128
|
+
.. admonition:: equality comparison
|
|
129
|
+
|
|
130
|
+
Efficiently compare two ``SplitEnds``.
|
|
131
|
+
|
|
132
|
+
:returns: ``True`` only if ``other`` is also a ``Splitend``
|
|
133
|
+
whose corresponding data to ``self`` compares as
|
|
134
|
+
equal and both share the same root node.
|
|
135
|
+
Otherwise ``False``.
|
|
136
|
+
|
|
137
|
+
"""
|
|
99
138
|
if not isinstance(other, type(self)):
|
|
100
139
|
return False
|
|
101
140
|
|
|
@@ -116,11 +155,55 @@ class SplitEnd[D]:
|
|
|
116
155
|
right = right.prev()
|
|
117
156
|
return True
|
|
118
157
|
|
|
158
|
+
def __repr__(self) -> str:
|
|
159
|
+
"""
|
|
160
|
+
.. admonition:: repr string
|
|
161
|
+
|
|
162
|
+
Construct string to reproduce the ``SplitEnd``.
|
|
163
|
+
TODO: Fix, does not include root node.
|
|
164
|
+
|
|
165
|
+
:returns: String to reproduce the ``SplitEnd``.
|
|
166
|
+
|
|
167
|
+
"""
|
|
168
|
+
return 'SplitEend(' + ', '.join(map(repr, reversed(self))) + ')'
|
|
169
|
+
|
|
170
|
+
def __str__(self) -> str:
|
|
171
|
+
"""
|
|
172
|
+
.. admonition:: user string
|
|
173
|
+
|
|
174
|
+
Construct string meaningful to an end user.
|
|
175
|
+
Does not give root node info.
|
|
176
|
+
|
|
177
|
+
:returns: String to reproduce the ``SplitEnd``.
|
|
178
|
+
|
|
179
|
+
"""
|
|
180
|
+
return '>< ' + ' -> '.join(map(str, self)) + ' ||'
|
|
181
|
+
|
|
182
|
+
def snip(self) -> D:
|
|
183
|
+
"""
|
|
184
|
+
.. admonition:: cut just tip
|
|
185
|
+
|
|
186
|
+
:returns: Data from snipped tip,
|
|
187
|
+
just return root data if at root.
|
|
188
|
+
|
|
189
|
+
"""
|
|
190
|
+
if self._count > 1:
|
|
191
|
+
data, self._end, self._count = self._end.both() + (self._count - 1,)
|
|
192
|
+
else:
|
|
193
|
+
data = self._end.data()
|
|
194
|
+
|
|
195
|
+
return data
|
|
196
|
+
|
|
119
197
|
def cut(self, num: int | None = None) -> tuple[D, ...]:
|
|
120
|
-
"""
|
|
198
|
+
"""
|
|
199
|
+
.. admonition:: Cut data off end of SplitEnd
|
|
200
|
+
|
|
201
|
+
Pop "cut" data off Split, just copy root data.
|
|
202
|
+
|
|
203
|
+
:param num: Optional number of nodes to cut,
|
|
204
|
+
default is entire stack.
|
|
205
|
+
:returns: Tuple of data cut off from end towards root.
|
|
121
206
|
|
|
122
|
-
:param num: Optional number of nodes to cut, default is entire stack.
|
|
123
|
-
:returns: Tuple of data cut off from end.
|
|
124
207
|
"""
|
|
125
208
|
if num is None or num > self._count:
|
|
126
209
|
num = self._count
|
|
@@ -142,42 +225,63 @@ class SplitEnd[D]:
|
|
|
142
225
|
return data
|
|
143
226
|
|
|
144
227
|
def extend(self, *ds: D) -> None:
|
|
145
|
-
"""
|
|
146
|
-
|
|
228
|
+
"""
|
|
229
|
+
.. admonition:: extend SplitEnd with data
|
|
230
|
+
|
|
231
|
+
Add data onto the tip of the SplitEnd. Like adding
|
|
232
|
+
a hair extension.
|
|
233
|
+
|
|
234
|
+
:param ds: data to extend the splitend
|
|
147
235
|
|
|
148
|
-
:param ds: data to extend the splitend
|
|
149
236
|
"""
|
|
150
237
|
for d in ds:
|
|
151
238
|
node = SENode(d, self._end)
|
|
152
239
|
self._end, self._count = node, self._count + 1
|
|
153
240
|
|
|
154
241
|
def peak(self) -> D:
|
|
155
|
-
"""
|
|
242
|
+
"""
|
|
243
|
+
.. admonition:: peak at end
|
|
244
|
+
|
|
245
|
+
Return the data at end (top) of the SplitEnd
|
|
246
|
+
without consuming it.
|
|
247
|
+
|
|
248
|
+
:returns: The data at the end (tip) of the SplitEnd.
|
|
156
249
|
|
|
157
|
-
:returns: The data at the end of the SplitEnd.
|
|
158
250
|
"""
|
|
159
251
|
return self._end.data()
|
|
160
252
|
|
|
161
253
|
def root(self) -> SENode[D]:
|
|
162
254
|
"""
|
|
163
|
-
|
|
255
|
+
.. admonition:: get root
|
|
256
|
+
|
|
257
|
+
:returns: The root SENode node of the SplitEnd.
|
|
258
|
+
|
|
164
259
|
"""
|
|
165
260
|
return self._root
|
|
166
261
|
|
|
167
262
|
def reroot(self, root: SENode[D]) -> 'SplitEnd[D]':
|
|
168
|
-
"""
|
|
263
|
+
"""
|
|
264
|
+
.. admonition:: re-root SplitEnd
|
|
265
|
+
|
|
266
|
+
Create a brand new SplitEnd with the same data but different root.
|
|
267
|
+
|
|
268
|
+
:returns: New SplitEnd with the same data
|
|
269
|
+
and the new ``root``.
|
|
270
|
+
:raises ValueError: If new and original root nodes
|
|
271
|
+
are not compatible.
|
|
272
|
+
|
|
273
|
+
.. note::
|
|
169
274
|
|
|
170
|
-
|
|
275
|
+
Two nodes are compatible root nodes if and only if
|
|
171
276
|
|
|
172
|
-
|
|
277
|
+
- they are both actually root nodes
|
|
173
278
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
279
|
+
- which implies that their previous nodes are themselves
|
|
280
|
+
|
|
281
|
+
- their data compare as equal
|
|
282
|
+
|
|
283
|
+
- comparing by identity is too strong
|
|
178
284
|
|
|
179
|
-
:returns: New SplitEnd with the same data and the new ``root``.
|
|
180
|
-
:raises ValueError: If new and original root nodes are not compatible.
|
|
181
285
|
"""
|
|
182
286
|
if not root:
|
|
183
287
|
msg = 'New root node is not a root node.'
|
|
@@ -193,22 +297,15 @@ class SplitEnd[D]:
|
|
|
193
297
|
|
|
194
298
|
return SplitEnd(*lifo, root = root)
|
|
195
299
|
|
|
196
|
-
def
|
|
197
|
-
"""Snip data off tip of SplitEnd. Just return data if tip is root.
|
|
198
|
-
|
|
199
|
-
:returns: Data snipped off tip, just return root data if at root.
|
|
300
|
+
def split(self, *ds: D) -> 'SplitEnd[D]':
|
|
200
301
|
"""
|
|
201
|
-
|
|
202
|
-
data, self._end, self._count = self._end.both() + (self._count - 1,)
|
|
203
|
-
else:
|
|
204
|
-
data = self._end.data()
|
|
302
|
+
.. admonition:: split
|
|
205
303
|
|
|
206
|
-
|
|
304
|
+
Split the end and add more data.
|
|
207
305
|
|
|
208
|
-
|
|
209
|
-
|
|
306
|
+
:returns: New instance, same data nodes plus
|
|
307
|
+
additional ones on end. Same root node.
|
|
210
308
|
|
|
211
|
-
:returns: New instance, same data nodes plus additional ones on end.
|
|
212
309
|
"""
|
|
213
310
|
se: SplitEnd[D] = SplitEnd(self._root.data())
|
|
214
311
|
se._count, se._end, se._root = self._count, self._end, self._root
|
|
@@ -221,11 +318,17 @@ class SplitEnd[D]:
|
|
|
221
318
|
def fold[T](self, f: Callable[[T, D], T], init: T) -> T: ...
|
|
222
319
|
|
|
223
320
|
def fold[T](self, f: Callable[[T, D], T], init: T | None = None) -> T:
|
|
224
|
-
"""
|
|
321
|
+
"""
|
|
322
|
+
.. admonition:: fold
|
|
323
|
+
|
|
324
|
+
Reduce with a function, folding from tip to root.
|
|
325
|
+
|
|
326
|
+
:param f: Folding function, first argument
|
|
327
|
+
is for the accumulator.
|
|
328
|
+
:param init: Optional initial starting value for the fold.
|
|
329
|
+
:returns: Reduced value folding from tip to root
|
|
330
|
+
in natural LIFO order.
|
|
225
331
|
|
|
226
|
-
:param f: Folding function, first argument is for the accumulator.
|
|
227
|
-
:param init: Optional initial starting value for the fold.
|
|
228
|
-
:returns: Reduced value folding from tip to root in natural LIFO order.
|
|
229
332
|
"""
|
|
230
333
|
if init is None:
|
|
231
334
|
return self._end.fold(f) # type: ignore
|
|
@@ -237,11 +340,16 @@ class SplitEnd[D]:
|
|
|
237
340
|
def rev_fold[T](self, f: Callable[[T, D], T], init: T) -> T: ...
|
|
238
341
|
|
|
239
342
|
def rev_fold[T](self, f: Callable[[T, D], T], init: T | None = None) -> T:
|
|
240
|
-
"""
|
|
343
|
+
"""
|
|
344
|
+
.. admonition:: reverse fold
|
|
345
|
+
|
|
346
|
+
Reduce with a function, fold from root to tip.
|
|
347
|
+
|
|
348
|
+
:param f: Folding function, first argument
|
|
349
|
+
is for the accumulator.
|
|
350
|
+
:param init: Optional initial starting value for the fold.
|
|
351
|
+
:returns: Reduced value folding from root to tip.
|
|
241
352
|
|
|
242
|
-
:param f: Folding function, first argument is for the accumulator.
|
|
243
|
-
:param init: Optional initial starting value for the fold.
|
|
244
|
-
:returns: Reduced value folding from root to tip.
|
|
245
353
|
"""
|
|
246
354
|
if init is None:
|
|
247
355
|
return cast(T, reduce_left(reversed(self), cast(Callable[[D, D], D], f)))
|
|
@@ -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.
|
|
@@ -13,21 +13,25 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
"""
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- root
|
|
29
|
-
|
|
30
|
-
|
|
16
|
+
.. admonition:: SplitEnd node
|
|
17
|
+
|
|
18
|
+
Used to make inwardly directed bush-like graphs.
|
|
19
|
+
|
|
20
|
+
- designed so multiple splitends can safely share the same data
|
|
21
|
+
- nodes always contain data
|
|
22
|
+
- data node ``SENode[D]`` make up end-to-root singularly linked lists
|
|
23
|
+
- two nodes compare as equal if
|
|
24
|
+
|
|
25
|
+
- both their previous Nodes are the same
|
|
26
|
+
- their data compare as equal
|
|
27
|
+
|
|
28
|
+
- a root node is a node whose previous node is itself
|
|
29
|
+
|
|
30
|
+
- root nodes mark the bottom of splitend stacks
|
|
31
|
+
|
|
32
|
+
- more than one node can point to the same proceeding node
|
|
33
|
+
|
|
34
|
+
- forming bush like graphs
|
|
31
35
|
|
|
32
36
|
"""
|
|
33
37
|
|
|
@@ -46,8 +50,12 @@ class SENode[D]:
|
|
|
46
50
|
|
|
47
51
|
def __init__(self, data: D, prev: Self | _Sentinel = _sentinel) -> None:
|
|
48
52
|
"""
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
.. admonition:: init
|
|
54
|
+
|
|
55
|
+
:param data: Nodes always contain data of type ``D``.
|
|
56
|
+
:param prev: Link to previous node, points to ``self``
|
|
57
|
+
if a root node.
|
|
58
|
+
|
|
51
59
|
"""
|
|
52
60
|
self._data = data
|
|
53
61
|
if prev is not _sentinel:
|
|
@@ -57,11 +65,20 @@ class SENode[D]:
|
|
|
57
65
|
|
|
58
66
|
def __bool__(self) -> bool:
|
|
59
67
|
"""
|
|
60
|
-
|
|
68
|
+
.. admonition:: bool
|
|
69
|
+
|
|
70
|
+
:returns: ``True`` only if ``SENode`` is not a root node.
|
|
71
|
+
|
|
61
72
|
"""
|
|
62
73
|
return self._prev is not self
|
|
63
74
|
|
|
64
75
|
def __iter__(self) -> Iterator[D]:
|
|
76
|
+
"""
|
|
77
|
+
.. admonition:: iter
|
|
78
|
+
|
|
79
|
+
:yields: Node date until and including root node.
|
|
80
|
+
|
|
81
|
+
"""
|
|
65
82
|
node = self
|
|
66
83
|
while node:
|
|
67
84
|
yield node._data
|
|
@@ -70,8 +87,19 @@ class SENode[D]:
|
|
|
70
87
|
|
|
71
88
|
def __eq__(self, other: object) -> bool:
|
|
72
89
|
"""
|
|
73
|
-
|
|
74
|
-
|
|
90
|
+
.. admonition:: equality comparison
|
|
91
|
+
|
|
92
|
+
Two ``SENodes`` nodes are equal if their previous nodes
|
|
93
|
+
are the same object and their data compare as equal.
|
|
94
|
+
|
|
95
|
+
:param other:
|
|
96
|
+
:returns: ``True`` if ``other`` is a SplitEnd which compares
|
|
97
|
+
as equal to ``self`` as described above.
|
|
98
|
+
|
|
99
|
+
.. note::
|
|
100
|
+
|
|
101
|
+
Will be useful when consolidating SplitEnds.
|
|
102
|
+
|
|
75
103
|
"""
|
|
76
104
|
if not isinstance(other, type(self)):
|
|
77
105
|
return False
|
|
@@ -82,32 +110,44 @@ class SENode[D]:
|
|
|
82
110
|
return True
|
|
83
111
|
return False
|
|
84
112
|
|
|
85
|
-
def
|
|
86
|
-
"""Peak at data and previous node, if a root then data and self.
|
|
87
|
-
|
|
88
|
-
:returns: tuple of type tuple[D, SENode[D]]
|
|
113
|
+
def data(self) -> D:
|
|
89
114
|
"""
|
|
90
|
-
|
|
115
|
+
.. admonition:: peak at data in SENode
|
|
91
116
|
|
|
92
|
-
|
|
93
|
-
"""Peak at data.
|
|
117
|
+
:returns: The data stored in the ``SENode``.
|
|
94
118
|
|
|
95
|
-
:returns: The data stored in the ``SENode``.
|
|
96
119
|
"""
|
|
97
120
|
return self._data
|
|
98
121
|
|
|
99
122
|
def prev(self) -> Self:
|
|
100
|
-
"""
|
|
123
|
+
"""
|
|
124
|
+
.. admonition:: peak at previous node
|
|
125
|
+
|
|
126
|
+
:returns: The previous node stored in the ``SENode``.
|
|
101
127
|
|
|
102
|
-
:returns: The previous node stored in the ``SENode``.
|
|
103
128
|
"""
|
|
104
129
|
return self._prev
|
|
105
130
|
|
|
131
|
+
def both(self) -> tuple[D, Self]:
|
|
132
|
+
"""
|
|
133
|
+
.. admonition:: both data and previous node
|
|
134
|
+
|
|
135
|
+
Peak at data and previous node,
|
|
136
|
+
if a root then data and ``self``.
|
|
137
|
+
|
|
138
|
+
:returns: Tuple of type ``tuple[D, SENode[D]]``
|
|
139
|
+
|
|
140
|
+
"""
|
|
141
|
+
return self._data, self._prev
|
|
142
|
+
|
|
106
143
|
def push(self, data: D) -> Self:
|
|
107
|
-
"""
|
|
144
|
+
"""
|
|
145
|
+
.. admonition:: push date to create new ``SENode[D]``
|
|
146
|
+
|
|
147
|
+
:param data: Data for new node to contain.
|
|
148
|
+
:returns: New ``SENode`` whose previous node is
|
|
149
|
+
the current node.
|
|
108
150
|
|
|
109
|
-
:param data: Data for new node to contain.
|
|
110
|
-
:returns: New ``SENode`` whose previous node is the current node.
|
|
111
151
|
"""
|
|
112
152
|
return cast(Self, SENode(data, self))
|
|
113
153
|
|
|
@@ -117,11 +157,17 @@ class SENode[D]:
|
|
|
117
157
|
def fold[T](self, f: Callable[[T, D], T], init: T) -> T: ...
|
|
118
158
|
|
|
119
159
|
def fold[T](self, f: Callable[[T, D], T], init: T | _Sentinel = _sentinel) -> T:
|
|
120
|
-
"""
|
|
160
|
+
"""
|
|
161
|
+
.. admonition:: fold
|
|
162
|
+
|
|
163
|
+
Fold data across linked nodes with a function.
|
|
164
|
+
|
|
165
|
+
:param f: Folding function, first argument
|
|
166
|
+
is for accumulated value.`
|
|
167
|
+
:param init: Optional initial starting value for the fold.
|
|
168
|
+
:returns: Reduced value folded from end to root
|
|
169
|
+
in natural LIFO order.
|
|
121
170
|
|
|
122
|
-
:param f: Folding function, first argument is for accumulated value.`
|
|
123
|
-
:param init: Optional initial starting value for the fold.
|
|
124
|
-
:returns: Reduced value folded from end to root in natural LIFO order.
|
|
125
171
|
"""
|
|
126
172
|
if init is not _sentinel:
|
|
127
173
|
acc = cast(T, init)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Copyright 2024-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
|
-
"""Mutable stack objects that can safely share data.
|
|
16
|
-
|
|
17
|
-
Python package implementing a singularly linked stack of nodes called a ``SplitEnd``.
|
|
18
|
-
While mutable, these data structures can safely share immutable data nodes between
|
|
19
|
-
themselves. These nodes, of type ``SENode``, always contain data. A root node is
|
|
20
|
-
a node whose previous node is itself. Root nodes mark the bottom of the stack.
|
|
21
|
-
|
|
22
|
-
Like one of many "split ends" from shafts of hair, a ``SplitEnd`` can be "snipped"
|
|
23
|
-
shorter or "extended" further from its "end" node at the top of the stack. Its "root"
|
|
24
|
-
node is at the bottom of the stack and can not be "snipped" or "cut" off.
|
|
25
|
-
|
|
26
|
-
A ``SplitEnd`` can be duplicated onto a compatible root node. By compatible, the
|
|
27
|
-
new root must contain data that compares as equal to the old root node.
|
|
28
|
-
|
|
29
|
-
TODO: A ``scalp`` is a container of root nodes all of whose contained data must be
|
|
30
|
-
hashable. The ``scalp.add(d)`` method will return the unique root node with data
|
|
31
|
-
that compares as equal to ``d``, or create a new root node if such a contained node
|
|
32
|
-
does not exist.
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
__author__ = 'Geoffrey R. Scheller'
|
|
36
|
-
__copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
|
|
37
|
-
__license__ = 'Apache License 2.0'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/src/pythonic_fp/splitends/splitend.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|