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.
Files changed (31) hide show
  1. pythonic_fp_splitends-2.0.3/.github/workflows/static.yml +64 -0
  2. {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/.gitignore +2 -0
  3. {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/CHANGELOG.rst +16 -0
  4. {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/PKG-INFO +17 -12
  5. {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/README.rst +9 -4
  6. pythonic_fp_splitends-2.0.3/docs/Makefile +78 -0
  7. pythonic_fp_splitends-2.0.3/docs/gen_conf.py +110 -0
  8. pythonic_fp_splitends-2.0.3/docs/requirements.txt +2 -0
  9. pythonic_fp_splitends-2.0.3/docs/source/_static/custom.css +3 -0
  10. pythonic_fp_splitends-2.0.3/docs/source/api/index.rst +15 -0
  11. pythonic_fp_splitends-2.0.3/docs/source/api/splitend.rst +8 -0
  12. pythonic_fp_splitends-2.0.3/docs/source/api/splitend_node.rst +8 -0
  13. pythonic_fp_splitends-2.0.3/docs/source/changelog.rst +7 -0
  14. pythonic_fp_splitends-2.0.3/docs/source/description.rst +7 -0
  15. pythonic_fp_splitends-2.0.3/docs/source/index.rst +30 -0
  16. pythonic_fp_splitends-2.0.3/docs/source/releases.rst +24 -0
  17. pythonic_fp_splitends-2.0.3/docs/source/usage.rst +21 -0
  18. {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/pyproject.toml +28 -20
  19. pythonic_fp_splitends-2.0.3/src/pythonic_fp/splitends/__init__.py +41 -0
  20. pythonic_fp_splitends-2.0.3/src/pythonic_fp/splitends/__init__.pyi +0 -0
  21. pythonic_fp_splitends-2.0.3/src/pythonic_fp/splitends/py.typed +0 -0
  22. {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/src/pythonic_fp/splitends/splitend.py +168 -60
  23. {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/src/pythonic_fp/splitends/splitend_node.py +84 -38
  24. pythonic_fp_splitends-2.0.0/src/pythonic_fp/splitends/__init__.py +0 -37
  25. {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/LICENSE +0 -0
  26. /pythonic_fp_splitends-2.0.0/src/pythonic_fp/splitends/__init__.pyi → /pythonic_fp_splitends-2.0.3/docs/source/_static/.gitkeep +0 -0
  27. /pythonic_fp_splitends-2.0.0/src/pythonic_fp/splitends/py.typed → /pythonic_fp_splitends-2.0.3/docs/source/_templates/.gitkeep +0 -0
  28. {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/src/pythonic_fp/splitends/splitend.pyi +0 -0
  29. {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/src/pythonic_fp/splitends/splitend_node.pyi +0 -0
  30. {pythonic_fp_splitends-2.0.0 → pythonic_fp_splitends-2.0.3}/tests/test_splitend.py +0 -0
  31. {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
@@ -1,6 +1,8 @@
1
1
  # Minimal version - only add to when necessary
2
2
  **/__pycache__/
3
3
  dist/
4
+ docs/build/
5
+ docs/source/conf.py
4
6
  **/.dmypy.json
5
7
  **/.mypy_cache/
6
8
  **/.pytest_cache/
@@ -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.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.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
15
  Classifier: Typing :: Typed
16
16
  License-File: LICENSE
17
- Requires-Dist: pythonic-fp-fptools>=5.1.2
18
- Requires-Dist: pythonic-fp-iterables>=5.1.2
19
- Requires-Dist: pythonic-fp-queues>=5.1.0
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/development/build/html/
23
- Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/homepage/build/html/
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 <https://pypi.org/project/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/homepage/build/html/index.html>`_
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/splitends/development/build/html>`_.
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-2025 Geoffrey R. Scheller. Licensed under the Apache
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 <https://pypi.org/project/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/homepage/build/html/index.html>`_
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/splitends/development/build/html>`_.
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-2025 Geoffrey R. Scheller. Licensed under the Apache
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,2 @@
1
+ sphinx>=9.1
2
+ furo
@@ -0,0 +1,3 @@
1
+ dl.field-list > dt {
2
+ text-transform: none !important;
3
+ }
@@ -0,0 +1,15 @@
1
+ Splitends
2
+ =========
3
+
4
+ .. automodule:: pythonic_fp.splitends
5
+ :no-members:
6
+ :ignore-module-all:
7
+ :no-index:
8
+
9
+ .. toctree::
10
+ :caption: Splitends
11
+ :maxdepth: 1
12
+
13
+ splitend
14
+ splitend_node
15
+
@@ -0,0 +1,8 @@
1
+ module splitend
2
+ ---------------
3
+
4
+ **Mutable LIFO stacks safely share immutable data between themselves.**
5
+
6
+ .. automodule:: pythonic_fp.splitends.splitend
7
+ :members:
8
+ :special-members:
@@ -0,0 +1,8 @@
1
+ module splitend_node
2
+ --------------------
3
+
4
+ **Immutable data nodes used by splitend stacks.**
5
+
6
+ .. automodule:: pythonic_fp.splitends.splitend_node
7
+ :members:
8
+ :special-members:
@@ -0,0 +1,7 @@
1
+ changelog
2
+ ---------
3
+
4
+ Change log for the
5
+ `pythonic-fp-splitends
6
+ <https://github.com/grscheller/pythonic-fp-splitends/blob/main/CHANGELOG.rst>`_
7
+ PyPI project.
@@ -0,0 +1,7 @@
1
+ Description
2
+ -----------
3
+
4
+ .. automodule:: pythonic_fp.splitends
5
+ :synopsis:
6
+ :noindex:
7
+
@@ -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.0"
7
+ version = "2.0.3"
4
8
  readme = "README.rst"
5
9
  requires-python = ">=3.13"
6
- license = { file = "LICENSE" }
7
- authors = [{ name = "Geoffrey R. Scheller", email = "geoffrey@scheller.com" }]
8
- keywords = ["SplitEnd", "lifo", "stack"]
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.13",
24
+ "Programming Language :: Python :: 3.14",
16
25
  "Typing :: Typed",
17
26
  ]
18
27
  dependencies = [
19
- "pythonic-fp-fptools>=5.1.2",
20
- "pythonic-fp-iterables>=5.1.2",
21
- "pythonic-fp-queues>=5.1.0",
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/development/build/html/"
28
- Homepage = "https://grscheller.github.io/pythonic-fp/homepage/build/html/"
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.ini_options]
61
- addopts = "-ra"
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 = "py313"
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/*" = ["C901"]
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'
@@ -1,4 +1,4 @@
1
- # Copyright 2023-2025 Geoffrey R. Scheller
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
- **LIFO stacks safely sharing immutable data.**
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
- - the "root" of a ``SplitEnd``
24
- - it is fixed and cannot be removed from the ``SplitEnd``
25
- - different mutable split ends can safely share the same "tail"
26
- - each ``SplitEnd`` sees itself as a singularly linked list
27
- - bush-like datastructures can be formed using multiple ``SplitEnds``
28
- - the ``SplitEnd.split`` and ``len`` methods are O(1)
29
- - in boolean context returns true if the ``SplitEnd`` is not just its "root"
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
- :param root_data: Irremovable initial data at bottom of stack.
54
- :param data: Removable data to be pushed onto splitend stack.
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
- :returns: ``True`` is ``SplitEnd`` is not just its root node.
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
- return self._count
118
+ """
119
+ .. admonition:: len
91
120
 
92
- def __repr__(self) -> str:
93
- return 'SplitEend(' + ', '.join(map(repr, reversed(self))) + ')'
121
+ :returns: The number of nodes on the ``Splitend``.
94
122
 
95
- def __str__(self) -> str:
96
- return '>< ' + ' -> '.join(map(str, self)) + ' ||'
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
- """Cut data off end of ``SplitEnd``.
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
- """Add data onto the tip of the SplitEnd. Like adding a hair
146
- extension.
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
- """Return the data at end (top) of SplitEnd without consuming it.
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
- :returns: The root SENode node of the SplitEnd.
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
- """Create a brand new SplitEnd with the same data but different root.
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
- .. note::
275
+ Two nodes are compatible root nodes if and only if
171
276
 
172
- Two nodes are compatible root nodes if and only if
277
+ - they are both actually root nodes
173
278
 
174
- - they are both actually root nodes
175
- - that is their previous nodes are themselves
176
- - their data compare as equal
177
- - comparing by identity is too strong for some use cases
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 snip(self) -> D:
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
- if self._count > 1:
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
- return data
304
+ Split the end and add more data.
207
305
 
208
- def split(self, *ds: D) -> 'SplitEnd[D]':
209
- """Split the end and add more data.
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
- """Reduce with a function, folding from tip to root.
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
- """Reduce with a function, fold from root to tip.
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-2025 Geoffrey R. Scheller
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
- Class SENode
17
- ============
18
-
19
- **Used to make inwardly directed bush-like graphs.**
20
-
21
- - designed so multiple splitends can safely share the same data
22
- - nodes always contain data
23
- - data node ``SENode[D]`` make up end-to-root singularly linked lists
24
- - two nodes compare as equal if
25
- - both their previous Nodes are the same
26
- - their data compare as equal
27
- - a root node is a node whose previous node is itself
28
- - root nodes mark the bottom of splitend stacks
29
- - more than one node can point to the same proceeding node
30
- - forming bush like graphs
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
- :param data: Nodes always contain data of type ``D``.
50
- :param prev: Link to previous node. Points to ``self`` if a root node.
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
- :returns: ``True`` if ``SENode`` is not a root node.
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
- Two ``SENodes`` nodes are equal if their previous nodes are the
74
- same object and their data compare as equal.
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 both(self) -> tuple[D, Self]:
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
- return self._data, self._prev
115
+ .. admonition:: peak at data in SENode
91
116
 
92
- def data(self) -> D:
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
- """Peak at previous node.
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
- """Create a new ``SENode[D]``.
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
- """Fold data across linked nodes with a function..
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'