├── .github └── workflows │ └── python-package.yml ├── .gitignore ├── CHANGES ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── docs ├── .gitignore ├── Makefile ├── conf.py ├── index.rst └── make.bat ├── examples └── lucky_numbers.py ├── setup.py ├── src ├── config.h ├── dllist.c ├── dllist.h ├── flags.h ├── llist.c ├── package │ ├── __init__.py │ ├── _llist.pyi │ └── py.typed ├── py23macros.h ├── sllist.c ├── sllist.h ├── utils.c └── utils.h └── tests ├── dllist_test.py ├── llist_test_case.py ├── py23_utils.py ├── sllist_test.py └── speed_test.py /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, build, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ${{ matrix.os }} 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | os: [ubuntu-latest, windows-latest] 21 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up Python ${{ matrix.python-version }} 26 | uses: actions/setup-python@v3 27 | with: 28 | python-version: ${{ matrix.python-version }} 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | python -m pip install build flake8 pytest 33 | - name: Build 34 | run: | 35 | python -m build 36 | - name: Lint with flake8 37 | run: | 38 | flake8 tests --count --select=E9,F63,F7 --show-source --statistics 39 | flake8 tests --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 40 | - name: Install package 41 | run: | 42 | python -m pip install . 43 | - name: Test with pytest 44 | run: | 45 | pytest 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | MANIFEST 2 | build 3 | dist 4 | *.egg-info 5 | *.pyc 6 | *.so 7 | *.swp 8 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | * llist-0.8 (unreleased) 2 | 3 | - added missing file to MANIFEST 4 | - adjusted tests to work with immortal objects from Python 3.12 5 | (closes issue #18) 6 | - added support for type hints (closes issue #16) 7 | - added missing PyObject_GC_UnTrack calls (contribution from 8 | Alexander Shadchin) 9 | 10 | ----------------------------------------------------------------------- 11 | 12 | * llist-0.7 (2021-04-27) 13 | 14 | - fixed repr() and str() for self-referencing lists and nodes 15 | (closes issue #10) 16 | - fixed duplicated release of Py_None during destruction of 17 | self-referencing lists and nodes (closes issue #11) 18 | - fixed a bug where a list item could be missed if it had been added 19 | during preceding step of list iteration (closes issue #12) 20 | - added insertbefore(), insertafter(), insertnodebefore() 21 | and insertnodeafter() methods to dllist (closes issue #14) 22 | - implemented iter() method on list iterators 23 | - implemented iternodes() and itervalues() methods on dllist and 24 | sllist (closes issue #5) 25 | 26 | ----------------------------------------------------------------------- 27 | 28 | * llist-0.6 (2018-06-30) 29 | 30 | - allow subclassing of list and node types 31 | - added appendnode() method to dllist and sllist (contribution from 32 | Alex Stennet) 33 | - added insertnode() method to dllist and 34 | insertnodeafter()/insertnodebefore() methods to sllist 35 | - support for cyclic garbabe collection 36 | 37 | ----------------------------------------------------------------------- 38 | 39 | * llist-0.5 (2017-11-18) 40 | 41 | - changed visibility of internal module functions to hidden 42 | - hash() function now returns different results for lists containing 43 | permuted elements 44 | - added owner attribute to nodes (contribution from Anand Aiyer) 45 | - added clearing of neighbour and owner references in removed node 46 | 47 | ----------------------------------------------------------------------- 48 | 49 | * llist-0.4 (2013-01-01) 50 | 51 | - Python 3.x support 52 | 53 | ----------------------------------------------------------------------- 54 | 55 | * llist-0.3 (2012-01-22) 56 | 57 | - fixed neighbour references (prev and next) in dangling nodes 58 | - implemented clear() method in dllist and sllist 59 | - implemented rotate() method in dllist and sllist 60 | - fixed reference counting of list weakrefs 61 | - fixed segmentation fault when removing a node that does not belong 62 | to the list (issue #1) 63 | - implemented extend(), extendleft() and extendright() methods in 64 | dllist and sllist 65 | - changed insert_before() to insertbefore() and insert_after() to 66 | insertafter() 67 | 68 | ----------------------------------------------------------------------- 69 | 70 | * llist-0.2 (2011-12-30) 71 | 72 | - subscript operator `lst[x]` now directly returns values stored 73 | in the list, not dllistnode objects 74 | - implemented nodeat() method in dllist and sllist 75 | - fixed segmentation faults in sllist.insert and sllist.delete 76 | methods 77 | - added missing Py_DECREFs to sllist 78 | - added concatenation and in-place concatenation operator 79 | - added repeat operator 80 | - added hash() support 81 | 82 | ----------------------------------------------------------------------- 83 | 84 | * llist-0.1 (2011-12-26) 85 | 86 | Initial release 87 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2018 Adam Jakubek, Rafał Gałczyński 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include Makefile CHANGES LICENSE MANIFEST MANIFEST.in README.md 2 | recursive-include src *.c *.h 3 | recursive-include docs *.py *.rst 4 | include docs/Makefile docs/make.bat 5 | graft docs/_static 6 | graft docs/_templates 7 | recursive-include examples *.py 8 | recursive-include tests *test*.py py23_utils.py 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build clean docs install test 2 | 3 | all: clean build test 4 | 5 | build: 6 | python setup.py build 7 | 8 | install: 9 | python setup.py install 10 | 11 | clean: 12 | cd docs && $(MAKE) $(MFLAGS) clean 13 | python setup.py clean --all 14 | 15 | docs: 16 | python setup.py install --install-lib ./docs 17 | cd docs && $(MAKE) $(MFLAGS) clean 18 | cd docs && $(MAKE) $(MFLAGS) doctest html 19 | cd docs/_build/html && zip -r docs.zip * 20 | 21 | test: 22 | python setup.py install --install-lib ./tests 23 | python tests/llist_test.py 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | llist - linked lists for CPython 2 | ================================ 3 | 4 | llist is an extension module for CPython providing basic linked list 5 | data structures. 6 | Collections implemented in the llist module perform well in problems 7 | which rely on fast insertions and/or deletions of elements in 8 | the middle of a sequence. 9 | For this kind of workload, they can be significantly faster than 10 | collections.deque or standard Python lists. 11 | 12 | This extension requires CPython 2.7 or newer (3.x is supported). 13 | If you are looking for an implementation of linked lists in pure Python, 14 | visit http://github.com/rgsoda/pypy-llist/ 15 | The pypy-llist module has the same API as this extension, but is 16 | significantly slower in CPython. 17 | 18 | Currently llist provides the following types of linked lists: 19 | - dllist - a doubly linked list 20 | - sllist - a singly linked list 21 | 22 | Full documentation of these classes is available at: 23 | https://ajakubek.github.io/python-llist/index.html 24 | 25 | To install this package, run "pip install llist". 26 | Alternatively you can also download it manually from http://pypi.python.org/pypi, unpack into a directory and build/install with the following commands: 27 | ``` 28 | python -m build 29 | pip install . 30 | ``` 31 | The instruction assumes that the 'build' frontend is already available in site-packages. 32 | 33 | The most current development version is available at: 34 | https://github.com/ajakubek/python-llist/ 35 | 36 | Bugs can be reported at: 37 | https://github.com/ajakubek/python-llist/issues 38 | 39 | This software is distributed under the MIT license. 40 | Please see the LICENSE file included in the package for details. 41 | 42 | [![Build Status](https://github.com/ajakubek/python-llist/actions/workflows/python-package.yml/badge.svg?branch=master)](https://github.com/ajakubek/python-llist/actions/workflows/python-package.yml?query=branch%3Amaster) 43 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _static 3 | _templates -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " singlehtml to make a single large HTML file" 22 | @echo " pickle to make pickle files" 23 | @echo " json to make JSON files" 24 | @echo " htmlhelp to make HTML files and a HTML help project" 25 | @echo " qthelp to make HTML files and a qthelp project" 26 | @echo " devhelp to make HTML files and a Devhelp project" 27 | @echo " epub to make an epub" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 29 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 30 | @echo " text to make text files" 31 | @echo " man to make manual pages" 32 | @echo " changes to make an overview of all changed/added/deprecated items" 33 | @echo " linkcheck to check all external links for integrity" 34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 35 | 36 | clean: 37 | -rm -rf $(BUILDDIR)/* 38 | 39 | html: 40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 41 | @echo 42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 43 | 44 | dirhtml: 45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 48 | 49 | singlehtml: 50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 51 | @echo 52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 53 | 54 | pickle: 55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 56 | @echo 57 | @echo "Build finished; now you can process the pickle files." 58 | 59 | json: 60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 61 | @echo 62 | @echo "Build finished; now you can process the JSON files." 63 | 64 | htmlhelp: 65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 66 | @echo 67 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 68 | ".hhp project file in $(BUILDDIR)/htmlhelp." 69 | 70 | qthelp: 71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 72 | @echo 73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/llist.qhcp" 76 | @echo "To view the help file:" 77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/llist.qhc" 78 | 79 | devhelp: 80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 81 | @echo 82 | @echo "Build finished." 83 | @echo "To view the help file:" 84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/llist" 85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/llist" 86 | @echo "# devhelp" 87 | 88 | epub: 89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 90 | @echo 91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 92 | 93 | latex: 94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 95 | @echo 96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 98 | "(use \`make latexpdf' here to do that automatically)." 99 | 100 | latexpdf: 101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 102 | @echo "Running LaTeX files through pdflatex..." 103 | make -C $(BUILDDIR)/latex all-pdf 104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 105 | 106 | text: 107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 108 | @echo 109 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 110 | 111 | man: 112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 113 | @echo 114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 115 | 116 | changes: 117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 118 | @echo 119 | @echo "The overview file is in $(BUILDDIR)/changes." 120 | 121 | linkcheck: 122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 123 | @echo 124 | @echo "Link check complete; look for any errors in the above output " \ 125 | "or in $(BUILDDIR)/linkcheck/output.txt." 126 | 127 | doctest: 128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 129 | @echo "Testing of doctests in the sources finished, look at the " \ 130 | "results in $(BUILDDIR)/doctest/output.txt." 131 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # llist documentation build configuration file, created by 4 | # sphinx-quickstart on Tue Dec 20 01:58:56 2011. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | #sys.path.insert(0, os.path.abspath('.')) 20 | sys.path.insert(0, os.path.abspath('.')) 21 | 22 | # -- General configuration ----------------------------------------------------- 23 | 24 | # If your documentation needs a minimal Sphinx version, state it here. 25 | #needs_sphinx = '1.0' 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be extensions 28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 29 | extensions = ['sphinx.ext.coverage', 'sphinx.ext.doctest'] 30 | # Add any paths that contain templates here, relative to this directory. 31 | templates_path = ['_templates'] 32 | 33 | # The suffix of source filenames. 34 | source_suffix = '.rst' 35 | 36 | # The encoding of source files. 37 | #source_encoding = 'utf-8-sig' 38 | 39 | # The master toctree document. 40 | master_doc = 'index' 41 | 42 | # General information about the project. 43 | project = u'llist' 44 | copyright = u'2011-2018 Adam Jakubek, Rafał Gałczyński' 45 | 46 | # The version info for the project you're documenting, acts as replacement for 47 | # |version| and |release|, also used in various other places throughout the 48 | # built documents. 49 | # 50 | # The short X.Y version. 51 | version = '0.8' 52 | # The full version, including alpha/beta/rc tags. 53 | release = '0.8' 54 | 55 | # The language for content autogenerated by Sphinx. Refer to documentation 56 | # for a list of supported languages. 57 | #language = None 58 | 59 | # There are two options for replacing |today|: either, you set today to some 60 | # non-false value, then it is used: 61 | #today = '' 62 | # Else, today_fmt is used as the format for a strftime call. 63 | #today_fmt = '%B %d, %Y' 64 | 65 | # List of patterns, relative to source directory, that match files and 66 | # directories to ignore when looking for source files. 67 | exclude_patterns = ['_build'] 68 | 69 | # The reST default role (used for this markup: `text`) to use for all documents. 70 | #default_role = None 71 | 72 | # If true, '()' will be appended to :func: etc. cross-reference text. 73 | #add_function_parentheses = True 74 | 75 | # If true, the current module name will be prepended to all description 76 | # unit titles (such as .. function::). 77 | #add_module_names = True 78 | 79 | # If true, sectionauthor and moduleauthor directives will be shown in the 80 | # output. They are ignored by default. 81 | #show_authors = False 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = 'sphinx' 85 | 86 | # A list of ignored prefixes for module index sorting. 87 | #modindex_common_prefix = [] 88 | 89 | 90 | # -- Options for HTML output --------------------------------------------------- 91 | 92 | # The theme to use for HTML and HTML Help pages. See the documentation for 93 | # a list of builtin themes. 94 | html_theme = 'default' 95 | 96 | # Theme options are theme-specific and customize the look and feel of a theme 97 | # further. For a list of options available for each theme, see the 98 | # documentation. 99 | #html_theme_options = {} 100 | 101 | # Add any paths that contain custom themes here, relative to this directory. 102 | #html_theme_path = [] 103 | 104 | # The name for this set of Sphinx documents. If None, it defaults to 105 | # " v documentation". 106 | #html_title = None 107 | 108 | # A shorter title for the navigation bar. Default is the same as html_title. 109 | #html_short_title = None 110 | 111 | # The name of an image file (relative to this directory) to place at the top 112 | # of the sidebar. 113 | #html_logo = None 114 | 115 | # The name of an image file (within the static path) to use as favicon of the 116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 117 | # pixels large. 118 | #html_favicon = None 119 | 120 | # Add any paths that contain custom static files (such as style sheets) here, 121 | # relative to this directory. They are copied after the builtin static files, 122 | # so a file named "default.css" will overwrite the builtin "default.css". 123 | html_static_path = ['_static'] 124 | 125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 126 | # using the given strftime format. 127 | #html_last_updated_fmt = '%b %d, %Y' 128 | 129 | # If true, SmartyPants will be used to convert quotes and dashes to 130 | # typographically correct entities. 131 | #html_use_smartypants = True 132 | 133 | # Custom sidebar templates, maps document names to template names. 134 | #html_sidebars = {} 135 | 136 | # Additional templates that should be rendered to pages, maps page names to 137 | # template names. 138 | #html_additional_pages = {} 139 | 140 | # If false, no module index is generated. 141 | #html_domain_indices = True 142 | 143 | # If false, no index is generated. 144 | #html_use_index = True 145 | 146 | # If true, the index is split into individual pages for each letter. 147 | #html_split_index = False 148 | 149 | # If true, links to the reST sources are added to the pages. 150 | #html_show_sourcelink = True 151 | 152 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 153 | #html_show_sphinx = True 154 | 155 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 156 | #html_show_copyright = True 157 | 158 | # If true, an OpenSearch description file will be output, and all pages will 159 | # contain a tag referring to it. The value of this option must be the 160 | # base URL from which the finished HTML is served. 161 | #html_use_opensearch = '' 162 | 163 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 164 | #html_file_suffix = None 165 | 166 | # Output file base name for HTML help builder. 167 | htmlhelp_basename = 'llistdoc' 168 | 169 | 170 | # -- Options for LaTeX output -------------------------------------------------- 171 | 172 | # The paper size ('letter' or 'a4'). 173 | #latex_paper_size = 'letter' 174 | 175 | # The font size ('10pt', '11pt' or '12pt'). 176 | #latex_font_size = '10pt' 177 | 178 | # Grouping the document tree into LaTeX files. List of tuples 179 | # (source start file, target name, title, author, documentclass [howto/manual]). 180 | latex_documents = [ 181 | ('index', 'llist.tex', u'llist Documentation', 182 | u'Adam Jakubek, Rafał Gałczyński', 'manual'), 183 | ] 184 | 185 | # The name of an image file (relative to this directory) to place at the top of 186 | # the title page. 187 | #latex_logo = None 188 | 189 | # For "manual" documents, if this is true, then toplevel headings are parts, 190 | # not chapters. 191 | #latex_use_parts = False 192 | 193 | # If true, show page references after internal links. 194 | #latex_show_pagerefs = False 195 | 196 | # If true, show URL addresses after external links. 197 | #latex_show_urls = False 198 | 199 | # Additional stuff for the LaTeX preamble. 200 | #latex_preamble = '' 201 | 202 | # Documents to append as an appendix to all manuals. 203 | #latex_appendices = [] 204 | 205 | # If false, no module index is generated. 206 | #latex_domain_indices = True 207 | 208 | 209 | # -- Options for manual page output -------------------------------------------- 210 | 211 | # One entry per manual page. List of tuples 212 | # (source start file, name, description, authors, manual section). 213 | man_pages = [ 214 | ('index', 'llist', u'llist Documentation', 215 | [u'Adam Jakubek, Rafał Gałczyński'], 1) 216 | ] 217 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. llist documentation master file, created by 2 | sphinx-quickstart on Tue Dec 20 01:58:56 2011. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | :mod:`llist` --- Linked list datatypes for Python 7 | ================================================= 8 | 9 | .. module:: llist 10 | :synopsis: Linked list datatypes for Python 11 | 12 | .. moduleauthor:: Adam Jakubek 13 | .. moduleauthor:: Rafał Gałczyński 14 | 15 | This module implements linked list data structures. 16 | Currently two types of lists are supported: a doubly linked :class:`dllist` 17 | and a singly linked :class:`sllist`. 18 | 19 | All data types defined in this module support efficient O(1) insertion 20 | and removal of elements (except removal in :class:`sllist` which is O(n)). 21 | Random access to elements using index is O(n). 22 | 23 | 24 | :class:`dllist` objects 25 | ----------------------- 26 | 27 | .. class:: dllist([iterable]) 28 | 29 | Return a new doubly linked list initialized with elements from *iterable*. 30 | If *iterable* is not specified, the new :class:`dllist` is empty. 31 | 32 | dllist objects provide the following attributes: 33 | 34 | .. attribute:: first 35 | 36 | First :class:`dllistnode` object in the list. `None` if list is empty. 37 | This attribute is read-only. 38 | 39 | .. attribute:: last 40 | 41 | Last :class:`dllistnode` object in the list. `None` if list is empty. 42 | This attribute is read-only. 43 | 44 | .. attribute:: size 45 | 46 | Number of elements in the list. 0 if list is empty. 47 | This attribute is read-only. 48 | 49 | dllist objects also support the following methods (all methods below have 50 | O(1) time complexity unless specifically documented otherwise): 51 | 52 | .. method:: append(x) 53 | 54 | Add *x* to the right side of the list and return inserted 55 | :class:`dllistnode`. 56 | 57 | Argument *x* might be a :class:`dllistnode`. In that case a new 58 | node will be created and initialized with the value extracted from *x*. 59 | 60 | .. method:: appendleft(x) 61 | 62 | Add *x* to the left side of the list and return inserted 63 | :class:`dllistnode`. 64 | 65 | Argument *x* might be a :class:`dllistnode`. In that case a new 66 | node will be created and initialized with the value extracted from *x*. 67 | 68 | .. method:: appendright(x) 69 | 70 | Add *x* to the right side of the list and return inserted 71 | :class:`dllistnode` (synonymous with :meth:`append`). 72 | 73 | Argument *x* might be a :class:`dllistnode`. In that case a new 74 | node will be created and initialized with the value extracted from *x*. 75 | 76 | .. method:: appendnode(node) 77 | 78 | Add *node* to the end of the list. The node must not belong to a list. 79 | 80 | The difference between :meth:`dllist.appendright()` and this method is 81 | that the former will repack the value from the argument into a new node 82 | while the latter will insert the passed node into the list. 83 | This makes :meth:`dllist.appendnode()` useful when a subclassed node type 84 | must be added to a list. 85 | 86 | Raises :exc:`TypeError` if *node* is not of type :class:`dllistnode`. 87 | 88 | Raises :exc:`ValueError` if *node* already belongs to a list. 89 | 90 | .. method:: clear() 91 | 92 | Remove all nodes from the list. 93 | 94 | .. method:: extend(iterable) 95 | 96 | Append elements from *iterable* to the right side of the list. 97 | 98 | .. method:: extendleft(iterable) 99 | 100 | Append elements from *iterable* to the left side of the list. 101 | Note that elements will be appended in reversed order. 102 | 103 | .. method:: extendright(iterable) 104 | 105 | Append elements from *iterable* to the right side of the list 106 | (synonymous with :meth:`extend`). 107 | 108 | .. method:: insert(x, [before]) 109 | 110 | Add *x* to the right side of the list if *before* is not specified, 111 | or insert *x* to the left side of :class:`dllistnode` *before*. 112 | Return inserted :class:`dllistnode`. 113 | 114 | Argument *x* might be a :class:`dllistnode`. In that case a new 115 | node will be created and initialized with the value extracted from *x*. 116 | 117 | Raises :exc:`TypeError` if *before* is not of type 118 | :class:`dllistnode`. 119 | 120 | Raises :exc:`ValueError` if *before* does not belong to *self*. 121 | 122 | .. method:: insertafter(x, ref) 123 | 124 | Insert *x* after *ref* and return inserted :class:`dllistnode`. 125 | 126 | Argument *x* might be a :class:`dllistnode`. In that case a new 127 | node will be created and initialized with the value extracted from *x*. 128 | 129 | Raises :exc:`TypeError` if *ref* is not of type :class:`dllistnode`. 130 | 131 | Raises :exc:`ValueError` if *ref* does not belong to *self*. 132 | 133 | This method has O(1) complexity. 134 | 135 | .. method:: insertbefore(x, ref) 136 | 137 | Insert *x* before *ref* and return inserted :class:`dllistnode`. 138 | 139 | Argument *x* might be a :class:`dllistnode`. In that case a new 140 | node will be created and initialized with the value extracted from *x*. 141 | 142 | Raises :exc:`TypeError` if *ref* is not of type :class:`dllistnode`. 143 | 144 | Raises :exc:`ValueError` if *ref* does not belong to *self*. 145 | 146 | This method has O(1) complexity. 147 | 148 | .. method:: insertnode(node, [before]) 149 | 150 | Add *node* to the right side of the list if *before* is not specified, 151 | or insert *node* to the left side of :class:`dllistnode` *before*. 152 | Return inserted :class:`dllistnode`. 153 | 154 | Argument *node* must be a :class:`dllistnode` object which does not belong 155 | to any list. The node will be inserted directly into *self*. 156 | This makes :meth:`dllist.insertnode()` useful when a subclassed node type 157 | must be added to a list. 158 | 159 | Raises :exc:`TypeError` if *node* or *before* is not of type 160 | :class:`dllistnode`. 161 | 162 | Raises :exc:`ValueError` if *node* belongs to a list or *before* 163 | does not belong to *self*. 164 | 165 | .. method:: insertnodeafter(node, ref) 166 | 167 | Insert *node* after *ref* and return inserted :class:`dllistnode`. 168 | 169 | Argument *node* must be a :class:`dllistnode` object which does not 170 | belong to any list. The node will be inserted directly into *self*. 171 | This makes :meth:`dllist.insertnodebefore()` useful when a subclassed node 172 | type must be added to a list. 173 | 174 | Raises :exc:`TypeError` if *ref* is not of type :class:`dllistnode`. 175 | 176 | Raises :exc:`ValueError` if *node* belongs to a list or *ref* does not 177 | belong to *self*. 178 | 179 | This method has O(1) complexity. 180 | 181 | .. method:: insertnodebefore(node, ref) 182 | 183 | Insert *node* before *ref* and return inserted :class:`dllistnode`. 184 | 185 | Argument *node* must be a :class:`dllistnode` object which does not 186 | belong to any list. The node will be inserted directly into *self*. 187 | This makes :meth:`dllist.insertnodebefore()` useful when a subclassed node 188 | type must be added to a list. 189 | 190 | Raises :exc:`TypeError` if *ref* is not of type :class:`dllistnode`. 191 | 192 | Raises :exc:`ValueError` if *node* belongs to a list or *ref* does not 193 | belong to *self*. 194 | 195 | This method has O(1) complexity. 196 | 197 | .. method:: iternodes() 198 | 199 | Return iterator over all nodes in the list. 200 | 201 | .. method:: itervalues() 202 | 203 | Return iterator over all values in the list. 204 | 205 | Equivalent to ``iter(lst)``. 206 | 207 | .. method:: nodeat(index) 208 | 209 | Return node (of type :class:`dllistnode`) at *index*. 210 | Negative indices are allowed (to count nodes from the right). 211 | 212 | Raises :exc:`TypeError` if *index* is not an integer. 213 | 214 | Raises :exc:`IndexError` if *index* is out of range. 215 | 216 | This method has O(n) complexity, but most recently accessed node is 217 | cached, so that accessing its neighbours is O(1). 218 | Note that inserting/deleting a node in the middle of the list will 219 | invalidate this cache. 220 | 221 | .. method:: pop() 222 | 223 | Remove and return an element's value from the right side of the list. 224 | 225 | Raises :exc:`ValueError` if *self* is empty. 226 | 227 | .. method:: popleft() 228 | 229 | Remove and return an element's value from the left side of the list. 230 | 231 | Raises :exc:`ValueError` if *self* is empty. 232 | 233 | .. method:: popright() 234 | 235 | Remove and return an element's value from the right side of the list 236 | (synonymous with :meth:`pop`). 237 | 238 | Raises :exc:`ValueError` if *self* is empty. 239 | 240 | .. method:: remove(node) 241 | 242 | Remove *node* from the list and return the element which was 243 | stored in it. 244 | 245 | Raises :exc:`TypeError` if *node* is not of type :class:`dllistnode`. 246 | 247 | Raises :exc:`ValueError` if *self* is empty, or *node* does 248 | not belong to *self*. 249 | 250 | .. method:: rotate(n) 251 | 252 | Rotate the list *n* steps to the right. If *n* is negative, rotate 253 | to the left. If *n* is 0, do nothing. 254 | 255 | Raises :exc:`TypeError` if *n* is not an integer. 256 | 257 | This method has O(n) time complexity (with regards to the size of 258 | the list). 259 | 260 | 261 | In addition to these methods, :class:`dllist` supports iteration, 262 | ``cmp(lst1, lst2)``, rich comparison operators, constant time ``len(lst)``, 263 | ``hash(lst)`` and subscript references ``lst[1234]`` for accessing elements 264 | by index. 265 | 266 | Indexed access has O(n) complexity, but most recently accessed node is 267 | cached, so that accessing its neighbours is O(1). 268 | Note that inserting/deleting a node in the middle of the list will 269 | invalidate this cache. 270 | 271 | Subscript references like ``v = lst[1234]`` return values stored in nodes. 272 | Negative indices are allowed (to count nodes from the right). 273 | 274 | Iteration over :class:`dllist` elements (using *for* or list 275 | comprehensions) will also directly yield values stored in nodes. 276 | 277 | Like most containers, :class:`dllist` objects can be extended using 278 | ``lst1 + lst2`` and ``lst * num`` syntax (including in-place ``+=`` 279 | and ``*=`` variants of these operators). 280 | 281 | Example: 282 | 283 | .. doctest:: 284 | 285 | >>> from llist import dllist, dllistnode 286 | 287 | >>> empty_lst = dllist() # create an empty list 288 | >>> print(empty_lst) 289 | dllist() 290 | 291 | >>> print(len(empty_lst)) # display length of the list 292 | 0 293 | >>> print(empty_lst.size) 294 | 0 295 | 296 | >>> print(empty_lst.first) # display the first node (nonexistent) 297 | None 298 | >>> print(empty_lst.last) # display the last node (nonexistent) 299 | None 300 | 301 | >>> lst = dllist([1, 2, 3]) # create and initialize a list 302 | >>> print(lst) # display elements in the list 303 | dllist([1, 2, 3]) 304 | 305 | >>> print(len(lst)) # display length of the list 306 | 3 307 | >>> print(lst.size) 308 | 3 309 | 310 | >>> print(lst.nodeat(0)) # access nodes by index 311 | dllistnode(1) 312 | >>> print(lst.nodeat(1)) 313 | dllistnode(2) 314 | >>> print(lst.nodeat(2)) 315 | dllistnode(3) 316 | 317 | >>> print(lst[0]) # access elements by index 318 | 1 319 | >>> print(lst[1]) 320 | 2 321 | >>> print(lst[2]) 322 | 3 323 | 324 | >>> node = lst.first # get the first node (same as lst[0]) 325 | >>> print(node) 326 | dllistnode(1) 327 | 328 | >>> print(node.value) # get value of node 329 | 1 330 | >>> print(node()) # get value of node 331 | 1 332 | >>> print(node.prev) # get the previous node (nonexistent) 333 | None 334 | >>> print(node.next) # get the next node 335 | dllistnode(2) 336 | >>> print(node.next.value) # get value of the next node 337 | 2 338 | 339 | >>> for value in lst: # iterate over values in list 340 | ... print(value * 2) 341 | 2 342 | 4 343 | 6 344 | 345 | >>> for value in lst.itervalues(): # iterate over values in list explicitly 346 | ... print(value * 2) 347 | 2 348 | 4 349 | 6 350 | 351 | >>> for node in lst.iternodes(): # iterate over nodes in list 352 | ... print(node.value * 2) 353 | 2 354 | 4 355 | 6 356 | 357 | >>> lst.appendright(4) # append value to the right side of the list 358 | 359 | >>> print(lst) 360 | dllist([1, 2, 3, 4]) 361 | >>> new_node = dllistnode(5) 362 | >>> lst.appendright(new_node) # append value from a node 363 | 364 | >>> print(lst) 365 | dllist([1, 2, 3, 4, 5]) 366 | >>> lst.appendleft(0) # append value to the left side of the list 367 | 368 | >>> print(lst) 369 | dllist([0, 1, 2, 3, 4, 5]) 370 | >>> new_node = dllistnode(6) 371 | >>> lst.appendnode(new_node) # append node to the end of the list 372 | 373 | >>> print(lst) 374 | dllist([0, 1, 2, 3, 4, 5, 6]) 375 | >>> print(lst.last is new_node) 376 | True 377 | 378 | >>> lst.extendright([7, 8, 9]) # right-extend list with elements from iterable 379 | >>> print(lst) 380 | dllist([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 381 | >>> lst.extendleft([-1, -2, -3]) # left-extend list with elements from iterable 382 | >>> print(lst) 383 | dllist([-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 384 | 385 | >>> lst = dllist([0, 1, 2, 3, 4, 5]) 386 | >>> node = lst.nodeat(2) 387 | >>> lst.insert(1.5, node) # insert 1.5 before node 388 | 389 | >>> print(lst) 390 | dllist([0, 1, 1.5, 2, 3, 4, 5]) 391 | >>> lst.insert(6) # append value to the right side of the list 392 | 393 | >>> print(lst) 394 | dllist([0, 1, 1.5, 2, 3, 4, 5, 6]) 395 | >>> new_node = dllistnode(2.5) 396 | >>> ref_node = lst.nodeat(4) 397 | >>> lst.insertnode(new_node, ref_node) # insert new_node before ref_node 398 | 399 | >>> print(lst) 400 | dllist([0, 1, 1.5, 2, 2.5, 3, 4, 5, 6]) 401 | >>> new_node = dllistnode(6.5) 402 | >>> lst.insertnode(new_node) # insert new_node at the end of the list 403 | 404 | >>> print(lst) 405 | dllist([0, 1, 1.5, 2, 2.5, 3, 4, 5, 6, 6.5]) 406 | >>> ref_node = lst.nodeat(7) 407 | >>> lst.insertbefore(4.5, ref_node) 408 | 409 | >>> print(lst) 410 | dllist([0, 1, 1.5, 2, 2.5, 3, 4, 4.5, 5, 6, 6.5]) 411 | >>> ref_node = lst.nodeat(9) 412 | >>> lst.insertbefore(5.5, ref_node) 413 | 414 | >>> print(lst) 415 | dllist([0, 1, 1.5, 2, 2.5, 3, 4, 4.5, 5, 5.5, 6, 6.5]) 416 | >>> new_node = dllistnode(0.5) 417 | >>> ref_node = lst.nodeat(1) 418 | >>> lst.insertnodebefore(new_node, ref_node) 419 | 420 | >>> print(lst) 421 | dllist([0, 0.5, 1, 1.5, 2, 2.5, 3, 4, 4.5, 5, 5.5, 6, 6.5]) 422 | >>> new_node = dllistnode(3.5) 423 | >>> ref_node = lst.nodeat(6) 424 | >>> lst.insertnodeafter(new_node, ref_node) 425 | 426 | >>> print(lst) 427 | dllist([0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5]) 428 | 429 | >>> lst.popleft() # remove leftmost node from the list 430 | 0 431 | >>> print(lst) 432 | dllist([0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5]) 433 | >>> lst.popright() # remove rightmost node from the list 434 | 6.5 435 | >>> print(lst) 436 | dllist([0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6]) 437 | >>> node = lst.nodeat(2) 438 | >>> lst.remove(node) # remove 3rd node from the list 439 | 1.5 440 | >>> print(lst) 441 | dllist([0.5, 1, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6]) 442 | >>> foreign_node = dllistnode() # create an unassigned node 443 | >>> lst.remove(foreign_node) # try to remove node not present in the list 444 | Traceback (most recent call last): 445 | File "/usr/lib/python2.6/doctest.py", line 1253, in __run 446 | compileflags, 1) in test.globs 447 | File "", line 1, in 448 | lst.remove(foreign_node) 449 | ValueError: dllistnode belongs to another list 450 | >>> lst.clear() 451 | >>> print(lst) 452 | dllist() 453 | 454 | >>> lst = dllist([1, 2, 3, 4, 5]) 455 | >>> lst.rotate(2) 456 | >>> print(lst) 457 | dllist([4, 5, 1, 2, 3]) 458 | >>> lst = dllist([1, 2, 3, 4, 5]) 459 | >>> lst.rotate(-2) 460 | >>> print(lst) 461 | dllist([3, 4, 5, 1, 2]) 462 | 463 | >>> dllist() == dllist([]) # list comparison (lexicographical order) 464 | True 465 | >>> dllist() != dllist([]) 466 | False 467 | >>> dllist([1, 2, 3]) < dllist([1, 3, 3]) 468 | True 469 | >>> dllist([1, 2]) > dllist([1, 2, 3]) 470 | False 471 | >>> dllist([1, 2, 3]) <= dllist() 472 | False 473 | >>> dllist([1, 2, 3]) >= dllist([1, 2, 3]) 474 | True 475 | 476 | >>> lst1 = dllist([1, 2, 3, 4]) # extending lists 477 | >>> lst2 = dllist([5, 6, 7, 8]) 478 | >>> ext_lst = lst1 + lst2 479 | >>> print(ext_lst) 480 | dllist([1, 2, 3, 4, 5, 6, 7, 8]) 481 | 482 | >>> lst = dllist([1, 2, 3, 4]) 483 | >>> ext_lst = lst * 2 484 | >>> print(ext_lst) 485 | dllist([1, 2, 3, 4, 1, 2, 3, 4]) 486 | 487 | >>> lst = dllist([0]) 488 | >>> node = lst.first 489 | >>> weak_ref = node.owner # get reference to the list which owns the node 490 | >>> print(weak_ref() is lst) # call the reference to obtain the actual list 491 | True 492 | >>> del lst 493 | >>> print(weak_ref()) # None is returned if list does not exist 494 | None 495 | 496 | 497 | :class:`dllistnode` objects 498 | --------------------------- 499 | 500 | .. class:: dllistnode([value]) 501 | 502 | Return a new doubly linked list node, initialized (optionally) 503 | with *value*. 504 | 505 | dllistnode objects provide the following attributes: 506 | 507 | .. attribute:: next 508 | 509 | Next node in the list. This attribute is read-only. 510 | 511 | .. attribute:: prev 512 | 513 | Previous node in the list. This attribute is read-only. 514 | 515 | .. attribute:: value 516 | 517 | Value stored in this node. 518 | 519 | .. attribute:: owner 520 | 521 | Weak reference to the list which owns this node. This attribute is read-only. 522 | It is possible for nodes to outlive the list they belong to. If the list 523 | is no longer alive, calling the `owner` reference will return `None`. 524 | 525 | Note that value stored in the node can also be obtained through 526 | the :meth:`__call__()` method (using standard ``node()`` syntax). 527 | 528 | 529 | :class:`dllistiterator` objects 530 | ------------------------------- 531 | 532 | .. class:: dllistiterator 533 | 534 | Return a new doubly linked list iterator. 535 | 536 | dllistiterator objects are not meant to be created by user. 537 | They are returned by the :meth:`dllist.__iter__()` method to hold 538 | iteration state. 539 | 540 | Note that iteration using :class:`dllistiterator` interface will 541 | directly yield values stored in nodes, not :class:`dllistnode` 542 | objects. 543 | 544 | Example: 545 | 546 | .. doctest:: 547 | 548 | >>> from llist import dllist 549 | >>> lst = dllist([1, 2, 3]) 550 | >>> for value in lst: 551 | ... print(value * 2) 552 | 2 553 | 4 554 | 6 555 | 556 | 557 | :class:`sllist` objects 558 | ----------------------- 559 | 560 | .. class:: sllist([iterable]) 561 | 562 | Return a new singly linked list initialized with elements from *iterable*. 563 | If *iterable* is not specified, the new :class:`sllist` is empty. 564 | 565 | sllist objects provide the following attributes: 566 | 567 | .. attribute:: first 568 | 569 | First :class:`sllistnode` object in the list. `None` if list is empty. 570 | This attribute is read-only. 571 | 572 | .. attribute:: last 573 | 574 | Last :class:`sllistnode` object in the list. `None` if list is empty. 575 | This attribute is read-only. 576 | 577 | .. attribute:: size 578 | 579 | Number of elements in the list. 0 if list is empty. 580 | This attribute is read-only. 581 | 582 | sllist objects also support the following methods: 583 | 584 | .. method:: append(x) 585 | 586 | Add *x* to the right side of the list and return inserted 587 | :class:`sllistnode`. 588 | 589 | Argument *x* might be a :class:`sllistnode`. In that case a new 590 | node will be created and initialized with the value extracted from *x*. 591 | 592 | This method has O(1) complexity. 593 | 594 | .. method:: appendleft(x) 595 | 596 | Add *x* to the left side of the list and return inserted 597 | :class:`sllistnode`. 598 | 599 | Argument *x* might be a :class:`sllistnode`. In that case a new 600 | node will be created and initialized with the value extracted from *x*. 601 | 602 | This method has O(1) complexity. 603 | 604 | .. method:: appendright(x) 605 | 606 | Add *x* to the right side of the list and return inserted 607 | :class:`sllistnode`. 608 | 609 | Argument *x* might be a :class:`sllistnode`. In that case a new 610 | node will be created and initialized with the value extracted from *x*. 611 | 612 | This method has O(1) complexity. 613 | 614 | .. method:: appendnode(node) 615 | 616 | Add *node* to the end of the list. The node must not belong to a list. 617 | 618 | The difference between :meth:`sllist.appendright()` and this method is 619 | that the former will repack the value from the argument into a new node 620 | while the latter will insert the passed node into the list. 621 | This makes :meth:`sllist.appendnode()` useful when a subclassed node type 622 | must be added to a list. 623 | 624 | Raises :exc:`TypeError` if *node* is not of type :class:`sllistnode`. 625 | 626 | Raises :exc:`ValueError` if *node* already belongs to a list. 627 | 628 | .. method:: clear() 629 | 630 | Remove all nodes from the list. 631 | 632 | .. method:: extend(iterable) 633 | 634 | Append elements from *iterable* to the right side of the list. 635 | 636 | This method has O(n) complexity (in the size of *iterable*). 637 | 638 | .. method:: extendleft(iterable) 639 | 640 | Append elements from *iterable* to the left side of the list. 641 | Note that elements will be appended in reversed order. 642 | 643 | This method has O(n) complexity (in the size of *iterable*). 644 | 645 | .. method:: extendright(iterable) 646 | 647 | Append elements from *iterable* to the right side of the list 648 | (synonymous with :meth:`extend`). 649 | 650 | This method has O(n) complexity (in the size of *iterable*). 651 | 652 | .. method:: insertafter(x, ref) 653 | 654 | Insert *x* after *ref* and return inserted :class:`sllistnode`. 655 | 656 | Argument *x* might be a :class:`sllistnode`. In that case a new 657 | node will be created and initialized with the value extracted from *x*. 658 | 659 | Raises :exc:`TypeError` if *ref* is not of type :class:`sllistnode`. 660 | 661 | Raises :exc:`ValueError` if *ref* does not belong to *self*. 662 | 663 | This method has O(1) complexity. 664 | 665 | .. method:: insertbefore(x, ref) 666 | 667 | Insert *x* before *ref* and return inserted :class:`sllistnode`. 668 | 669 | Argument *x* might be a :class:`sllistnode`. In that case a new 670 | node will be created and initialized with the value extracted from *x*. 671 | 672 | Raises :exc:`TypeError` if *ref* is not of type :class:`sllistnode`. 673 | 674 | Raises :exc:`ValueError` if *ref* does not belong to *self*. 675 | 676 | This method has O(n) complexity. 677 | 678 | .. method:: insertnodeafter(node, ref) 679 | 680 | Add *node* to the right side of *ref*. 681 | Return inserted :class:`sllistnode`. 682 | 683 | Argument *node* must be a :class:`sllistnode` instance which does not 684 | belong to any list. The node will be inserted directly into *self*. 685 | This makes :meth:`sllist.insertnodeafter()` useful when a subclassed node 686 | type must be added to a list. 687 | 688 | Raises :exc:`TypeError` if *node* or *ref* is not of type 689 | :class:`sllistnode`. 690 | 691 | Raises :exc:`ValueError` if *node* belongs to another list or *ref* does 692 | not belong to *self*. 693 | 694 | .. method:: insertnodebefore(node, ref) 695 | 696 | Add *node* to the left side of *ref*. 697 | Return inserted :class:`sllistnode`. 698 | 699 | Argument *node* must be a :class:`sllistnode` instance which does not 700 | belong to any list. The node will be inserted directly into *self*. 701 | This makes :meth:`sllist.insertnodebefore()` useful when a subclassed node 702 | type must be added to a list. 703 | 704 | Raises :exc:`TypeError` if *node* or *ref* is not of type 705 | :class:`sllistnode`. 706 | 707 | Raises :exc:`ValueError` if *node* belongs to another list or *ref* does 708 | not belong to *self*. 709 | 710 | .. method:: iternodes() 711 | 712 | Return iterator over all nodes in the list. 713 | 714 | .. method:: itervalues() 715 | 716 | Return iterator over all values in the list. 717 | 718 | Equivalent to ``iter(lst)``. 719 | 720 | .. method:: nodeat(index) 721 | 722 | Return node (of type :class:`sllistnode`) at *index*. 723 | Negative indices are allowed (to count nodes from the right). 724 | 725 | Raises :exc:`TypeError` if *index* is not an integer. 726 | 727 | Raises :exc:`IndexError` if *index* is out of range. 728 | 729 | This method has O(n) complexity. 730 | 731 | .. method:: pop() 732 | 733 | Remove and return an element's value from the right side of the list. 734 | 735 | Raises :exc:`ValueError` if *self* is empty. 736 | 737 | This method has O(n) time complexity. 738 | 739 | .. method:: popleft() 740 | 741 | Remove and return an element's value from the left side of the list. 742 | 743 | Raises :exc:`ValueError` if *self* is empty. 744 | 745 | This method has O(1) time complexity. 746 | 747 | .. method:: popright() 748 | 749 | Remove and return an element's value from the right side of the list. 750 | 751 | Raises :exc:`ValueError` if *self* is empty. 752 | 753 | This method has O(n) time complexity. 754 | 755 | .. method:: remove(node) 756 | 757 | Remove *node* from the list. 758 | 759 | Raises :exc:`TypeError` if *node* is not of type :class:`sllistnode`. 760 | 761 | Raises :exc:`ValueError` if *self* is empty, or *node* does 762 | not belong to *self*. 763 | 764 | This method has O(n) time complexity. 765 | 766 | .. method:: rotate(n) 767 | 768 | Rotate the list *n* steps to the right. If *n* is negative, rotate 769 | to the left. If *n* is 0, do nothing. 770 | 771 | Raises :exc:`TypeError` if *n* is not an integer. 772 | 773 | This method has O(n) time complexity (with regards to the size of 774 | the list). 775 | 776 | 777 | In addition to these methods, :class:`sllist` supports iteration, 778 | ``cmp(lst1, lst2)``, rich comparison operators, constant time ``len(lst)``, 779 | ``hash(lst)`` and subscript references ``lst[1234]`` for accessing elements 780 | by index. 781 | 782 | Subscript references like ``v = lst[1234]`` return values stored in nodes. 783 | Negative indices are allowed (to count nodes from the right). 784 | 785 | Iteration over :class:`sllist` elements (using *for* or list 786 | comprehensions) will also directly yield values stored in nodes. 787 | 788 | Like most containers, :class:`sllist` objects can be extended using 789 | ``lst1 + lst2`` and ``lst * num`` syntax (including in-place ``+=`` 790 | and ``*=`` variants of these operators). 791 | 792 | Example: 793 | 794 | .. doctest:: 795 | 796 | >>> from llist import sllist, sllistnode 797 | 798 | >>> empty_lst = sllist() # create an empty list 799 | >>> print(empty_lst) 800 | sllist() 801 | 802 | >>> print(len(empty_lst)) # display length of the list 803 | 0 804 | >>> print(empty_lst.size) 805 | 0 806 | 807 | >>> print(empty_lst.first) # display the first node (nonexistent) 808 | None 809 | >>> print(empty_lst.last) # display the last node (nonexistent) 810 | None 811 | 812 | >>> lst = sllist([1, 2, 3]) # create and initialize a list 813 | >>> print(lst) # display elements in the list 814 | sllist([1, 2, 3]) 815 | 816 | >>> print(len(lst)) # display length of the list 817 | 3 818 | >>> print(lst.size) 819 | 3 820 | 821 | >>> print(lst.nodeat(0)) # access nodes by index 822 | sllistnode(1) 823 | >>> print(lst.nodeat(1)) 824 | sllistnode(2) 825 | >>> print(lst.nodeat(2)) 826 | sllistnode(3) 827 | 828 | >>> print(lst[0]) # access elements by index 829 | 1 830 | >>> print(lst[1]) 831 | 2 832 | >>> print(lst[2]) 833 | 3 834 | 835 | >>> node = lst.first # get the first node (same as lst[0]) 836 | >>> print(node) 837 | sllistnode(1) 838 | 839 | >>> print(node.value) # get value of node 840 | 1 841 | >>> print(node()) # get value of node 842 | 1 843 | >>> print(node.next) # get the next node 844 | sllistnode(2) 845 | >>> print(node.next.value) # get value of the next node 846 | 2 847 | 848 | >>> for value in lst: # iterate over list elements 849 | ... print(value * 2) 850 | 2 851 | 4 852 | 6 853 | 854 | >>> for value in lst.itervalues(): # iterate over values in list explicitly 855 | ... print(value * 2) 856 | 2 857 | 4 858 | 6 859 | 860 | >>> for node in lst.iternodes(): # iterate over nodes in list 861 | ... print(node.value * 2) 862 | 2 863 | 4 864 | 6 865 | 866 | >>> lst.appendright(4) # append value to the right side of the list 867 | 868 | >>> print(lst) 869 | sllist([1, 2, 3, 4]) 870 | >>> new_node = sllistnode(5) 871 | >>> lst.appendright(new_node) # append value from a node 872 | 873 | >>> print(lst) 874 | sllist([1, 2, 3, 4, 5]) 875 | >>> lst.appendleft(0) # append value to the left side of the list 876 | 877 | >>> print(lst) 878 | sllist([0, 1, 2, 3, 4, 5]) 879 | >>> new_node = sllistnode(6) 880 | >>> lst.appendnode(new_node) # append node to the end of the list 881 | 882 | >>> print(lst) 883 | sllist([0, 1, 2, 3, 4, 5, 6]) 884 | >>> print(lst.last is new_node) 885 | True 886 | 887 | >>> lst.extendright([7, 8, 9]) # right-extend list with elements from iterable 888 | >>> print(lst) 889 | sllist([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 890 | >>> lst.extendleft([-1, -2, -3]) # left-extend list with elements from iterable 891 | >>> print(lst) 892 | sllist([-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 893 | 894 | >>> lst = sllist([0, 1, 2, 3, 4, 5]) 895 | >>> node = lst.nodeat(2) 896 | >>> lst.insertbefore(1.5, node) # insert 1.5 before node 897 | 898 | >>> print(lst) 899 | sllist([0, 1, 1.5, 2, 3, 4, 5]) 900 | >>> lst.insertafter(2.5, node) # insert 2.5 after node 901 | 902 | >>> print(lst) 903 | sllist([0, 1, 1.5, 2, 2.5, 3, 4, 5]) 904 | >>> new_node = sllistnode(3.5) 905 | >>> ref_node = lst.nodeat(6) 906 | >>> lst.insertnodebefore(new_node, ref_node) # insert new_node before ref_node 907 | 908 | >>> print(lst) 909 | sllist([0, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5]) 910 | >>> new_node = sllistnode(4.5) 911 | >>> ref_node = lst.nodeat(7) 912 | >>> lst.insertnodeafter(new_node, ref_node) # insert new_node after ref_node 913 | 914 | >>> print(lst) 915 | sllist([0, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5]) 916 | 917 | >>> lst.popleft() # remove leftmost node from the list 918 | 0 919 | >>> print(lst) 920 | sllist([1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5]) 921 | >>> lst.popright() # remove rightmost node from the list 922 | 5 923 | >>> print(lst) 924 | sllist([1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5]) 925 | >>> node = lst.nodeat(1) 926 | >>> lst.remove(node) # remove 2nd node from the list 927 | 1.5 928 | >>> print(lst) 929 | sllist([1, 2, 2.5, 3, 3.5, 4, 4.5]) 930 | >>> foreign_node = sllistnode() # create an unassigned node 931 | >>> lst.remove(foreign_node) # try to remove node not present in the list 932 | Traceback (most recent call last): 933 | File "/usr/lib/python2.6/doctest.py", line 1253, in __run 934 | compileflags, 1) in test.globs 935 | File "", line 1, in 936 | lst.remove(foreign_node) 937 | ValueError: sllistnode belongs to another list 938 | >>> lst.clear() 939 | >>> print(lst) 940 | sllist() 941 | 942 | >>> lst = sllist([1, 2, 3, 4, 5]) 943 | >>> lst.rotate(2) 944 | >>> print(lst) 945 | sllist([4, 5, 1, 2, 3]) 946 | >>> lst = sllist([1, 2, 3, 4, 5]) 947 | >>> lst.rotate(-2) 948 | >>> print(lst) 949 | sllist([3, 4, 5, 1, 2]) 950 | 951 | >>> sllist() == sllist([]) # list comparison (lexicographical order) 952 | True 953 | >>> sllist() != sllist([]) 954 | False 955 | >>> sllist([1, 2, 3]) < sllist([1, 3, 3]) 956 | True 957 | >>> sllist([1, 2]) > sllist([1, 2, 3]) 958 | False 959 | >>> sllist([1, 2, 3]) <= sllist() 960 | False 961 | >>> sllist([1, 2, 3]) >= sllist([1, 2, 3]) 962 | True 963 | 964 | >>> lst1 = sllist([1, 2, 3, 4]) # extending lists 965 | >>> lst2 = sllist([5, 6, 7, 8]) 966 | >>> ext_lst = lst1 + lst2 967 | >>> print(ext_lst) 968 | sllist([1, 2, 3, 4, 5, 6, 7, 8]) 969 | 970 | >>> lst = sllist([1, 2, 3, 4]) 971 | >>> ext_lst = lst * 2 972 | >>> print(ext_lst) 973 | sllist([1, 2, 3, 4, 1, 2, 3, 4]) 974 | 975 | >>> lst = sllist([0]) 976 | >>> node = lst.first 977 | >>> weak_ref = node.owner # get reference to the list which owns the node 978 | >>> print(weak_ref() is lst) # call the reference to obtain the actual list 979 | True 980 | >>> del lst 981 | >>> print(weak_ref()) # None is returned if list does not exist 982 | None 983 | 984 | 985 | :class:`sllistnode` objects 986 | --------------------------- 987 | 988 | .. class:: sllistnode([value]) 989 | 990 | Return a new singly linked list node, initialized (optionally) 991 | with *value*. 992 | 993 | sllistnode objects provide the following attributes: 994 | 995 | .. attribute:: next 996 | 997 | Next node in the list. This attribute is read-only. 998 | 999 | .. attribute:: value 1000 | 1001 | Value stored in this node. 1002 | 1003 | .. attribute:: owner 1004 | 1005 | Weak reference to the list which owns this node. This attribute is read-only. 1006 | It is possible for nodes to outlive the list they belong to. If the list 1007 | is no longer alive, calling the `owner` reference will return `None`. 1008 | 1009 | Note that value stored in the node can also be obtained through 1010 | the :meth:`__call__()` method (using standard ``node()`` syntax). 1011 | 1012 | 1013 | :class:`sllistiterator` objects 1014 | ------------------------------- 1015 | 1016 | .. class:: sllistiterator 1017 | 1018 | Return a new singly linked list iterator. 1019 | 1020 | sllistiterator objects are not meant to be created by user. 1021 | They are returned by the :meth:`sllist.__iter__()` method to hold 1022 | iteration state. 1023 | 1024 | Note that iteration using :class:`sllistiterator` interface will 1025 | directly yield values stored in nodes, not :class:`sllistnode` 1026 | objects. 1027 | 1028 | Example: 1029 | 1030 | .. doctest:: 1031 | 1032 | >>> from llist import sllist 1033 | >>> lst = sllist([1, 2, 3]) 1034 | >>> for value in lst: 1035 | ... print(value * 2) 1036 | 2 1037 | 4 1038 | 6 1039 | 1040 | 1041 | Changes 1042 | ======= 1043 | 1044 | .. include:: ../CHANGES 1045 | 1046 | 1047 | Copyright 1048 | ========= 1049 | 1050 | This module is copyrighted by Adam Jakubek and Rafał Gałczyński. 1051 | 1052 | It is distributed under the MIT license. Please see the LICENSE file 1053 | included in this package for more details. 1054 | 1055 | 1056 | Indices and tables 1057 | ================== 1058 | 1059 | * :ref:`genindex` 1060 | * :ref:`modindex` 1061 | * :ref:`search` 1062 | 1063 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | if NOT "%PAPER%" == "" ( 11 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 12 | ) 13 | 14 | if "%1" == "" goto help 15 | 16 | if "%1" == "help" ( 17 | :help 18 | echo.Please use `make ^` where ^ is one of 19 | echo. html to make standalone HTML files 20 | echo. dirhtml to make HTML files named index.html in directories 21 | echo. singlehtml to make a single large HTML file 22 | echo. pickle to make pickle files 23 | echo. json to make JSON files 24 | echo. htmlhelp to make HTML files and a HTML help project 25 | echo. qthelp to make HTML files and a qthelp project 26 | echo. devhelp to make HTML files and a Devhelp project 27 | echo. epub to make an epub 28 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 29 | echo. text to make text files 30 | echo. man to make manual pages 31 | echo. changes to make an overview over all changed/added/deprecated items 32 | echo. linkcheck to check all external links for integrity 33 | echo. doctest to run all doctests embedded in the documentation if enabled 34 | goto end 35 | ) 36 | 37 | if "%1" == "clean" ( 38 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 39 | del /q /s %BUILDDIR%\* 40 | goto end 41 | ) 42 | 43 | if "%1" == "html" ( 44 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 45 | if errorlevel 1 exit /b 1 46 | echo. 47 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 48 | goto end 49 | ) 50 | 51 | if "%1" == "dirhtml" ( 52 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 53 | if errorlevel 1 exit /b 1 54 | echo. 55 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 56 | goto end 57 | ) 58 | 59 | if "%1" == "singlehtml" ( 60 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 61 | if errorlevel 1 exit /b 1 62 | echo. 63 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 64 | goto end 65 | ) 66 | 67 | if "%1" == "pickle" ( 68 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 69 | if errorlevel 1 exit /b 1 70 | echo. 71 | echo.Build finished; now you can process the pickle files. 72 | goto end 73 | ) 74 | 75 | if "%1" == "json" ( 76 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished; now you can process the JSON files. 80 | goto end 81 | ) 82 | 83 | if "%1" == "htmlhelp" ( 84 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished; now you can run HTML Help Workshop with the ^ 88 | .hhp project file in %BUILDDIR%/htmlhelp. 89 | goto end 90 | ) 91 | 92 | if "%1" == "qthelp" ( 93 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 94 | if errorlevel 1 exit /b 1 95 | echo. 96 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 97 | .qhcp project file in %BUILDDIR%/qthelp, like this: 98 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\llist.qhcp 99 | echo.To view the help file: 100 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\llist.ghc 101 | goto end 102 | ) 103 | 104 | if "%1" == "devhelp" ( 105 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 106 | if errorlevel 1 exit /b 1 107 | echo. 108 | echo.Build finished. 109 | goto end 110 | ) 111 | 112 | if "%1" == "epub" ( 113 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 117 | goto end 118 | ) 119 | 120 | if "%1" == "latex" ( 121 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 122 | if errorlevel 1 exit /b 1 123 | echo. 124 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 125 | goto end 126 | ) 127 | 128 | if "%1" == "text" ( 129 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 130 | if errorlevel 1 exit /b 1 131 | echo. 132 | echo.Build finished. The text files are in %BUILDDIR%/text. 133 | goto end 134 | ) 135 | 136 | if "%1" == "man" ( 137 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 141 | goto end 142 | ) 143 | 144 | if "%1" == "changes" ( 145 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.The overview file is in %BUILDDIR%/changes. 149 | goto end 150 | ) 151 | 152 | if "%1" == "linkcheck" ( 153 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Link check complete; look for any errors in the above output ^ 157 | or in %BUILDDIR%/linkcheck/output.txt. 158 | goto end 159 | ) 160 | 161 | if "%1" == "doctest" ( 162 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 163 | if errorlevel 1 exit /b 1 164 | echo. 165 | echo.Testing of doctests in the sources finished, look at the ^ 166 | results in %BUILDDIR%/doctest/output.txt. 167 | goto end 168 | ) 169 | 170 | :end 171 | -------------------------------------------------------------------------------- /examples/lucky_numbers.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example compares performance of llist.dllist and collections.deque 3 | datatypes. 4 | It calculates a sequence of N lucky numbers 5 | (see http://en.wikipedia.org/wiki/Lucky_number for definition). 6 | The dominating operation for this problem is typically removal of elements 7 | from container, which is much faster with linked lists. 8 | """ 9 | 10 | from collections import deque 11 | from llist import dllist 12 | import sys 13 | import time 14 | 15 | N = 128 * 1024 16 | 17 | 18 | def generate_lucky_deque(max_num): 19 | lucky_numbers = deque(range(1, max_num + 1, 2)) 20 | 21 | multiple_node = lucky_numbers[1] 22 | multiple_pos = 2 23 | 24 | while multiple_node != None: 25 | removed_multiple = multiple_node 26 | if removed_multiple >= len(lucky_numbers): 27 | break 28 | 29 | removed_pos = removed_multiple - 1 30 | while removed_pos < len(lucky_numbers): 31 | del lucky_numbers[removed_pos] 32 | removed_pos += removed_multiple - 1 33 | 34 | multiple_node = lucky_numbers[multiple_pos] 35 | multiple_pos += 1 36 | 37 | return lucky_numbers 38 | 39 | 40 | def generate_lucky_list(max_num): 41 | lucky_numbers = dllist(range(1, max_num + 1, 2)) 42 | 43 | multiple_node = lucky_numbers.nodeat(1) 44 | 45 | while multiple_node != None: 46 | removed_multiple = multiple_node() 47 | if removed_multiple >= len(lucky_numbers): 48 | break 49 | 50 | removed_pos = removed_multiple - 1 51 | while removed_pos < len(lucky_numbers): 52 | del lucky_numbers[removed_pos] 53 | removed_pos += removed_multiple - 1 54 | 55 | multiple_node = multiple_node.next 56 | 57 | return lucky_numbers 58 | 59 | 60 | def time_execution(bench_func): 61 | start_time = time.clock() 62 | result = bench_func() 63 | elapsed = time.clock() - start_time 64 | return (result, elapsed) 65 | 66 | 67 | if __name__ == '__main__': 68 | sys.stdout.write('Calculating lucky numbers using deque... ') 69 | sys.stdout.flush() 70 | result_deque, time_deque = time_execution(lambda: generate_lucky_deque(N)) 71 | print('%gs' % time_deque) 72 | 73 | sys.stdout.write('Calculating lucky numbers using dllist... ') 74 | sys.stdout.flush() 75 | result_list, time_list = time_execution(lambda: generate_lucky_list(N)) 76 | print('%gs' % time_list) 77 | 78 | if list(result_deque) == list(result_list): 79 | print('Both results are equal.') 80 | else: 81 | print('Results differ, you might have found a bug!') 82 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | VERSION='0.8' 5 | 6 | from distutils.core import setup, Extension 7 | 8 | sources=[ 'src/llist.c', 9 | 'src/dllist.c', 10 | 'src/sllist.c', 11 | 'src/utils.c', 12 | ] 13 | 14 | setup(name='llist', 15 | description='Linked list data structures for Python', 16 | long_description=open('README.md').read(), 17 | author='Adam Jakubek, Rafał Gałczyński', 18 | author_email='ajakubek@gmail.com, rafal.galczynski@gmail.com', 19 | version=VERSION, 20 | url='https://github.com/ajakubek/python-llist', 21 | download_url='http://pypi.python.org/pypi/llist/%s' % VERSION, 22 | license='MIT', 23 | keywords='linked list, list', 24 | packages=['llist'], 25 | package_dir={'llist': 'src/package'}, 26 | package_data={'llist': ['_llist.pyi', 'py.typed']}, 27 | ext_modules=[Extension('llist._llist', sources)], 28 | classifiers=[ 29 | 'Development Status :: 3 - Alpha', 30 | 'Intended Audience :: Developers', 31 | 'License :: OSI Approved :: MIT License', 32 | 'Operating System :: MacOS :: MacOS X', 33 | 'Operating System :: Microsoft :: Windows', 34 | 'Operating System :: POSIX', 35 | 'Programming Language :: C', 36 | 'Programming Language :: Python :: 2', 37 | 'Programming Language :: Python :: 3', 38 | 'Programming Language :: Python :: Implementation :: CPython', 39 | 'Topic :: Software Development :: Libraries :: Python Modules', 40 | ], 41 | ) 42 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011-2018 Adam Jakubek, Rafał Gałczyński 2 | * Released under the MIT license (see attached LICENSE file). 3 | */ 4 | 5 | #ifndef CONFIG_H 6 | #define CONFIG_H 7 | 8 | #include 9 | 10 | #if ULONG_MAX > 0xffffffffUL 11 | #define HAVE_64_BIT_LONG 12 | #else 13 | #undef HAVE_64_BIT_LONG 14 | #endif 15 | 16 | #if defined(__GNUC__) && __GNUC__ >= 4 17 | #define LLIST_INTERNAL __attribute__((visibility("hidden"))) 18 | #else 19 | #define LLIST_INTERNAL 20 | #endif 21 | 22 | #endif /* CONFIG_H */ 23 | -------------------------------------------------------------------------------- /src/dllist.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011-2018 Adam Jakubek, Rafał Gałczyński 2 | * Released under the MIT license (see attached LICENSE file). 3 | */ 4 | 5 | #ifndef DLLIST_H 6 | #define DLLIST_H 7 | 8 | #include "config.h" 9 | 10 | LLIST_INTERNAL int dllist_init_type(void); 11 | LLIST_INTERNAL void dllist_register(PyObject* module); 12 | 13 | #endif /* DLLIST_H */ 14 | -------------------------------------------------------------------------------- /src/flags.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2019 Adam Jakubek, Rafał Gałczyński 2 | * Released under the MIT license (see attached LICENSE file). 3 | */ 4 | 5 | #ifndef FLAGS_H 6 | #define FLAGS_H 7 | 8 | #define LLIST_HAS_PY_NONE_REF (0x01) 9 | 10 | #endif /* FLAGS_H */ 11 | -------------------------------------------------------------------------------- /src/llist.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011-2018 Adam Jakubek, Rafał Gałczyński 2 | * Released under the MIT license (see attached LICENSE file). 3 | */ 4 | 5 | #include 6 | 7 | #include "sllist.h" 8 | #include "dllist.h" 9 | 10 | static PyMethodDef llist_methods[] = 11 | { 12 | { NULL } /* sentinel */ 13 | }; 14 | 15 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ 16 | #define PyMODINIT_FUNC void 17 | #endif 18 | 19 | #if PY_MAJOR_VERSION >= 3 20 | 21 | static struct PyModuleDef llist_moduledef = { 22 | PyModuleDef_HEAD_INIT, 23 | "_llist", /* m_name */ 24 | "Singly and doubly linked lists.", /* m_doc */ 25 | -1, /* m_size */ 26 | llist_methods, /* m_methods */ 27 | NULL, /* m_reload */ 28 | NULL, /* m_traverse */ 29 | NULL, /* m_clear */ 30 | NULL, /* m_free */ 31 | }; 32 | 33 | PyMODINIT_FUNC 34 | PyInit__llist(void) 35 | { 36 | PyObject* m; 37 | 38 | if (!sllist_init_type()) 39 | return NULL; 40 | if (!dllist_init_type()) 41 | return NULL; 42 | 43 | m = PyModule_Create(&llist_moduledef); 44 | 45 | sllist_register(m); 46 | dllist_register(m); 47 | 48 | return m; 49 | } 50 | 51 | #else 52 | 53 | PyMODINIT_FUNC 54 | init_llist(void) 55 | { 56 | PyObject* m; 57 | 58 | if (!sllist_init_type()) 59 | return; 60 | if (!dllist_init_type()) 61 | return; 62 | 63 | m = Py_InitModule3("_llist", llist_methods, 64 | "Singly and doubly linked lists."); 65 | 66 | sllist_register(m); 67 | dllist_register(m); 68 | } 69 | 70 | #endif /* PY_MAJOR_VERSION >= 3 */ 71 | -------------------------------------------------------------------------------- /src/package/__init__.py: -------------------------------------------------------------------------------- 1 | from llist._llist import ( 2 | dllist, 3 | dllistnode, 4 | dllistiterator, 5 | dllistnodeiterator, 6 | sllist, 7 | sllistnode, 8 | sllistiterator, 9 | sllistnodeiterator 10 | ) 11 | -------------------------------------------------------------------------------- /src/package/_llist.pyi: -------------------------------------------------------------------------------- 1 | import typing 2 | from collections.abc import Sequence 3 | from typing import Any, Generic, Optional, TypeVar 4 | from _typeshed import Incomplete as Incomplete 5 | 6 | 7 | T = TypeVar('T') 8 | 9 | 10 | class dllist: 11 | first: Optional[dllistnode[Any]] 12 | last: Optional[dllistnode[Any]] 13 | size: int 14 | 15 | @typing.overload 16 | def __init__(self) -> None: ... 17 | @typing.overload 18 | def __init__(self, items: Sequence[Any]) -> None: ... 19 | 20 | @typing.overload 21 | def append(self, value: T) -> dllistnode[T]: ... 22 | @typing.overload 23 | def append(self, node: dllistnode[T]) -> dllistnode[T]: ... 24 | 25 | @typing.overload 26 | def appendleft(self, value: T) -> dllistnode[T]: ... 27 | @typing.overload 28 | def appendleft(self, node: dllistnode[T]) -> dllistnode[T]: ... 29 | 30 | @typing.overload 31 | def appendright(self, value: T) -> dllistnode[T]: ... 32 | @typing.overload 33 | def appendright(self, node: dllistnode[T]) -> dllistnode[T]: ... 34 | 35 | def appendnode(self, node: dllistnode[T]) -> dllistnode[T]: ... 36 | 37 | def clear(self) -> None: ... 38 | 39 | def extend(self, items: Sequence[Any]) -> None: ... 40 | 41 | def extendleft(self, items: Sequence[Any]) -> None: ... 42 | 43 | def extendright(self, items: Sequence[Any]) -> None: ... 44 | 45 | @typing.overload 46 | def insert(self, value: T, before_ref: Optional[dllistnode[Any]] = None) -> dllistnode[T]: ... 47 | @typing.overload 48 | def insert(self, node: dllistnode[T], before_ref: Optional[dllistnode[Any]] = None) -> dllistnode[T]: ... 49 | 50 | @typing.overload 51 | def insertafter(self, value: T, after_ref: dllistnode[Any]) -> dllistnode[T]: ... 52 | @typing.overload 53 | def insertafter(self, node: dllistnode[T], after_ref: dllistnode[Any]) -> dllistnode[T]: ... 54 | 55 | @typing.overload 56 | def insertbefore(self, value: T, before_ref: dllistnode[Any]) -> dllistnode[T]: ... 57 | @typing.overload 58 | def insertbefore(self, node: dllistnode[T], before_ref: dllistnode[Any]) -> dllistnode[T]: ... 59 | 60 | def insertnode(self, node: dllistnode[T], before_ref: Optional[dllistnode[Any]] = None) -> dllistnode[T]: ... 61 | 62 | def insertnodeafter(self, node: dllistnode[T], after_ref: dllistnode[Any]) -> dllistnode[T]: ... 63 | 64 | def insertnodebefore(self, node: dllistnode[T], before_ref: dllistnode[Any]) -> dllistnode[T]: ... 65 | 66 | def iternodes(self) -> dllistnodeiterator: ... 67 | 68 | def itervalues(self) -> dllistiterator: ... 69 | 70 | def nodeat(self, index: int) -> dllistnode[Any]: ... 71 | 72 | def pop(self) -> Any: ... 73 | 74 | def popleft(self) -> Any: ... 75 | 76 | def popright(self) -> Any: ... 77 | 78 | def remove(self, node: dllistnode[T]) -> T: ... 79 | 80 | def rotate(self, n: int) -> None: ... 81 | 82 | def __add__(self, other: Any) -> None: ... 83 | def __delitem__(self, other: Any) -> None: ... 84 | def __eq__(self, other: Any) -> bool: ... 85 | def __ge__(self, other: Any) -> bool: ... 86 | def __getitem__(self, index: int) -> None: ... 87 | def __gt__(self, other: Any) -> bool: ... 88 | def __hash__(self) -> int: ... 89 | def __iadd__(self, other: Any) -> None: ... 90 | def __iter__(self) -> dllistiterator[Any]: ... 91 | def __le__(self, other: Any) -> bool: ... 92 | def __len__(self) -> int: ... 93 | def __lt__(self, other: Any) -> bool: ... 94 | def __mul__(self, other: Any) -> None: ... 95 | def __ne__(self, other: Any) -> bool: ... 96 | def __rmul__(self, other: Any) -> None: ... 97 | def __setitem__(self, index: int, value: Any) -> None: ... 98 | 99 | 100 | class dllistnode(Generic[T]): 101 | value: Optional[T] 102 | prev: Optional[dllistnode[Any]] 103 | next: Optional[dllistnode[Any]] 104 | owner: Optional[dllist] 105 | 106 | def __init__(self, *args, **kwargs) -> None: ... 107 | def __call__(self) -> Optional[T]: ... 108 | 109 | 110 | class dllistiterator: 111 | @classmethod 112 | def __init__(cls, *args, **kwargs) -> None: ... 113 | def __iter__(self) -> dllistiterator: ... 114 | def __next__(self) -> Any: ... 115 | 116 | 117 | class dllistnodeiterator: 118 | @classmethod 119 | def __init__(cls, *args, **kwargs) -> None: ... 120 | def __iter__(self) -> dllistnodeiterator: ... 121 | def __next__(self) -> dllistnode[Any]: ... 122 | 123 | 124 | class sllist: 125 | first: Optional[sllistnode[Any]] 126 | last: Optional[sllistnode[Any]] 127 | size: int 128 | 129 | @typing.overload 130 | def __init__(self) -> None: ... 131 | @typing.overload 132 | def __init__(self, items: Sequence[Any]) -> None: ... 133 | 134 | @typing.overload 135 | def append(self, value: T) -> sllistnode[T]: ... 136 | @typing.overload 137 | def append(self, node: sllistnode[T]) -> sllistnode[T]: ... 138 | 139 | @typing.overload 140 | def appendleft(self, value: T) -> sllistnode[T]: ... 141 | @typing.overload 142 | def appendleft(self, node: sllistnode[T]) -> sllistnode[T]: ... 143 | 144 | @typing.overload 145 | def appendright(self, value: T) -> sllistnode[T]: ... 146 | @typing.overload 147 | def appendright(self, node: sllistnode[T]) -> sllistnode[T]: ... 148 | 149 | def appendnode(self, node: sllistnode[T]) -> sllistnode[T]: ... 150 | 151 | def clear(self) -> None: ... 152 | 153 | def extend(self, items: Sequence[Any]) -> None: ... 154 | 155 | def extendleft(self, items: Sequence[Any]) -> None: ... 156 | 157 | def extendright(self, items: Sequence[Any]) -> None: ... 158 | 159 | @typing.overload 160 | def insertafter(self, value: T, after_ref: sllistnode[Any]) -> sllistnode[T]: ... 161 | @typing.overload 162 | def insertafter(self, node: sllistnode[T], after_ref: sllistnode[Any]) -> sllistnode[T]: ... 163 | 164 | @typing.overload 165 | def insertbefore(self, value: T, before_ref: sllistnode[Any]) -> sllistnode[T]: ... 166 | @typing.overload 167 | def insertbefore(self, node: sllistnode[T], before_ref: sllistnode[Any]) -> sllistnode[T]: ... 168 | 169 | def insertnodeafter(self, node: sllistnode[T], after_ref: sllistnode[Any]) -> sllistnode[T]: ... 170 | 171 | def insertnodebefore(self, node: sllistnode[T], before_ref: sllistnode[Any]) -> sllistnode[T]: ... 172 | 173 | def iternodes(self) -> sllistnodeiterator: ... 174 | 175 | def itervalues(self) -> sllistiterator: ... 176 | 177 | def nodeat(self, index: int) -> sllistnode[Any]: ... 178 | 179 | def pop(self) -> Any: ... 180 | 181 | def popleft(self) -> Any: ... 182 | 183 | def popright(self) -> Any: ... 184 | 185 | def remove(self, node: sllistnode[T]) -> T: ... 186 | 187 | def rotate(self, n: int) -> None: ... 188 | 189 | def __add__(self, other: Any) -> None: ... 190 | def __delitem__(self, other: Any) -> None: ... 191 | def __eq__(self, other: Any) -> bool: ... 192 | def __ge__(self, other: Any) -> bool: ... 193 | def __getitem__(self, index: int) -> None: ... 194 | def __gt__(self, other: Any) -> bool: ... 195 | def __hash__(self) -> int: ... 196 | def __iadd__(self, other: Any) -> None: ... 197 | def __iter__(self) -> sllistiterator[Any]: ... 198 | def __le__(self, other: object) -> bool: ... 199 | def __len__(self) -> int: ... 200 | def __lt__(self, other: Any) -> bool: ... 201 | def __mul__(self, other : Any) -> None: ... 202 | def __ne__(self, other: Any) -> bool: ... 203 | def __rmul__(self, other: Any) -> None: ... 204 | def __setitem__(self, index: int, value: Any) -> None: ... 205 | 206 | 207 | class sllistnode(Generic[T]): 208 | value: Optional[T] 209 | next: Optional[sllistnode[Any]] 210 | owner: Optional[sllist] 211 | 212 | def __init__(self, *args, **kwargs) -> None: ... 213 | def __call__(self) -> Optional[T]: ... 214 | 215 | 216 | class sllistiterator: 217 | @classmethod 218 | def __init__(cls, *args, **kwargs) -> None: ... 219 | def __iter__(self) -> sllistiterator: ... 220 | def __next__(self) -> Any: ... 221 | 222 | 223 | class sllistnodeiterator: 224 | @classmethod 225 | def __init__(cls, *args, **kwargs) -> None: ... 226 | def __iter__(self) -> sllistnodeiterator: ... 227 | def __next__(self) -> sllistnode[Any]: ... 228 | -------------------------------------------------------------------------------- /src/package/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajakubek/python-llist/5e8c79924c8549da1e4e3a319062b759780dea82/src/package/py.typed -------------------------------------------------------------------------------- /src/py23macros.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011-2018 Adam Jakubek, Rafał Gałczyński 2 | * Released under the MIT license (see attached LICENSE file). 3 | */ 4 | 5 | #ifndef MACROS_H 6 | #define MACROS_H 7 | 8 | #include 9 | 10 | 11 | #if PY_MAJOR_VERSION >= 3 12 | 13 | #define Py23String_FromString PyUnicode_FromString 14 | 15 | #define Py23String_Concat(left, right) \ 16 | do { \ 17 | PyObject* tmpConcatString; \ 18 | tmpConcatString = PyUnicode_Concat(*left, right); \ 19 | Py_DECREF(*left); \ 20 | *left = tmpConcatString; \ 21 | } while (0) 22 | 23 | #define Py23String_ConcatAndDel(left, right) \ 24 | do { \ 25 | PyObject* tmpConcatString; \ 26 | tmpConcatString = PyUnicode_Concat(*left, right); \ 27 | Py_DECREF(*left); \ 28 | Py_DECREF(right); \ 29 | *left = tmpConcatString; \ 30 | } while (0) 31 | 32 | #define Py23Int_Check PyLong_Check 33 | #define Py23Int_AsSsize_t PyLong_AsSsize_t 34 | 35 | #else 36 | 37 | #define Py23String_FromString PyString_FromString 38 | #define Py23String_Concat PyString_Concat 39 | #define Py23String_ConcatAndDel PyString_ConcatAndDel 40 | 41 | #define Py23Int_Check PyInt_Check 42 | #define Py23Int_AsSsize_t PyInt_AsSsize_t 43 | 44 | #endif /* PY_MAJOR_VERSION >= 3 */ 45 | 46 | #endif /* MACROS_H */ 47 | -------------------------------------------------------------------------------- /src/sllist.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011-2018 Adam Jakubek, Rafał Gałczyński 2 | * Released under the MIT license (see attached LICENSE file). 3 | */ 4 | 5 | #ifndef SLLIST_H 6 | #define SLLIST_H 7 | 8 | #include "config.h" 9 | 10 | LLIST_INTERNAL int sllist_init_type(void); 11 | LLIST_INTERNAL void sllist_register(PyObject* module); 12 | 13 | #endif /* SLLIST_H */ 14 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011-2018 Adam Jakubek, Rafał Gałczyński 2 | * Released under the MIT license (see attached LICENSE file). 3 | */ 4 | 5 | #include "config.h" 6 | 7 | #ifdef HAVE_64_BIT_LONG 8 | #define HASH_MIX_CONSTANT 0x9e3779b97f4a7c15 9 | #else 10 | #define HASH_MIX_CONSTANT 0x9e3779b9 11 | #endif 12 | 13 | LLIST_INTERNAL long hash_combine(long h1, long h2) 14 | { 15 | unsigned long uh1 = (unsigned long)h1; 16 | unsigned long uh2 = (unsigned long)h2; 17 | unsigned long c = uh1 ^ ((uh1 << 6) + (uh1 >> 2) + HASH_MIX_CONSTANT + uh2); 18 | return (long)c; 19 | } 20 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011-2018 Adam Jakubek, Rafał Gałczyński 2 | * Released under the MIT license (see attached LICENSE file). 3 | */ 4 | 5 | #ifndef UTILS_H 6 | #define UTILS_H 7 | 8 | #include "config.h" 9 | 10 | LLIST_INTERNAL long hash_combine(long h1, long h2); 11 | 12 | #endif /* UTILS_H */ 13 | -------------------------------------------------------------------------------- /tests/dllist_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | import weakref 5 | 6 | from llist_test_case import LListTestCase 7 | from py23_utils import py23_cmp, py23_range, py23_xrange 8 | 9 | from llist import dllist, dllistnode, sllistnode 10 | 11 | 12 | class testdllist(LListTestCase): 13 | 14 | def test_init_empty(self): 15 | ll = dllist() 16 | self.assertEqual(len(ll), 0) 17 | self.assertEqual(ll.size, 0) 18 | self.assertEqual(list(ll), []) 19 | 20 | def test_init_with_sequence(self): 21 | ref = py23_range(0, 1024, 4) 22 | ll = dllist(ref) 23 | self.assertEqual(len(ll), len(ref)) 24 | self.assertEqual(ll.size, len(ref)) 25 | self.assertEqual(list(ll), ref) 26 | 27 | def test_init_with_non_sequence(self): 28 | self.assertRaises(TypeError, dllist, None) 29 | self.assertRaises(TypeError, dllist, 1) 30 | self.assertRaises(TypeError, dllist, 1.5) 31 | 32 | def test_str(self): 33 | a = dllist([]) 34 | self.assertEqual(str(a), 'dllist()') 35 | b = dllist([None, 1, 'abc']) 36 | self.assertEqual(str(b), 'dllist([None, 1, abc])') 37 | 38 | def test_repr(self): 39 | a = dllist([]) 40 | self.assertEqual(repr(a), 'dllist()') 41 | b = dllist([None, 1, 'abc']) 42 | self.assertEqual(repr(b), 'dllist([None, 1, \'abc\'])') 43 | 44 | def test_node_str(self): 45 | a = dllist([None, None]).first 46 | self.assertEqual(str(a), 'dllistnode(None)') 47 | b = dllist([1, None]).first 48 | self.assertEqual(str(b), 'dllistnode(1)') 49 | c = dllist(['abc', None]).first 50 | self.assertEqual(str(c), 'dllistnode(abc)') 51 | 52 | def test_node_repr(self): 53 | a = dllist([None]).first 54 | self.assertEqual(repr(a), '') 55 | b = dllist([1, None]).first 56 | self.assertEqual(repr(b), '') 57 | c = dllist(['abc', None]).first 58 | self.assertEqual(repr(c), '') 59 | 60 | def test_str_recursive_list(self): 61 | ll = dllist() 62 | ll.append(dllistnode(ll)) 63 | self.assertEqual(str(ll), 'dllist([dllist(<...>)])') 64 | 65 | def test_str_recursive_node(self): 66 | ll = self.make_recursive_node_list() 67 | self.assertEqual(str(ll), 'dllist([dllistnode(dllistnode(<...>))])') 68 | 69 | def test_repr_recursive_list(self): 70 | ll = dllist() 71 | ll.append(dllistnode(ll)) 72 | self.assertEqual(repr(ll), 'dllist([dllist(<...>)])') 73 | 74 | def test_repr_recursive_node(self): 75 | ll = self.make_recursive_node_list() 76 | self.assertEqual(repr(ll), 'dllist([))>])') 77 | 78 | def make_recursive_node_list(self): 79 | ll = dllist() 80 | node = dllistnode() 81 | node.value = node 82 | ll.append(node) 83 | return ll 84 | 85 | def test_cmp(self): 86 | a = dllist(py23_xrange(0, 1100)) 87 | b = dllist(py23_xrange(0, 1101)) 88 | c = dllist([1, 2, 3, 4]) 89 | d = dllist([1, 2, 3, 5]) 90 | e = dllist([1, 0, 0, 0]) 91 | f = dllist([0, 0, 0, 0]) 92 | self.assertEqual(py23_cmp(a, a), 0) 93 | self.assertEqual(py23_cmp(a, b), -1) 94 | self.assertEqual(py23_cmp(b, a), 1) 95 | self.assertEqual(py23_cmp(c, d), -1) 96 | self.assertEqual(py23_cmp(d, c), 1) 97 | self.assertEqual(py23_cmp(e, f), 1) 98 | self.assertEqual(py23_cmp(f, e), -1) 99 | 100 | def test_cmp_nonlist(self): 101 | a = dllist(py23_xrange(0, 1100)) 102 | b = [py23_xrange(0, 1100)] 103 | if sys.hexversion < 0x03000000: 104 | # actual order is not specified by language 105 | self.assertNotEqual(py23_cmp(a, b), 0) 106 | self.assertNotEqual(py23_cmp(b, a), 0) 107 | self.assertNotEqual(py23_cmp([], a), 0) 108 | self.assertNotEqual(py23_cmp(a, []), 0) 109 | 110 | def test_eq(self): 111 | a = dllist(py23_xrange(0, 1100)) 112 | b = dllist(py23_xrange(0, 1101)) 113 | c = dllist([1, 2, 3, 4]) 114 | d = dllist([1, 2, 3, 5]) 115 | e = dllist([1, 0, 0, 0]) 116 | f = dllist([0, 0, 0, 0]) 117 | self.assertTrue(dllist() == dllist()) 118 | self.assertTrue(a == a) 119 | self.assertFalse(dllist() == a) 120 | self.assertFalse(a == dllist()) 121 | self.assertFalse(a == b) 122 | self.assertFalse(b == a) 123 | self.assertFalse(c == d) 124 | self.assertFalse(d == c) 125 | self.assertFalse(e == f) 126 | self.assertFalse(f == e) 127 | 128 | def test_ne(self): 129 | a = dllist(py23_xrange(0, 1100)) 130 | b = dllist(py23_xrange(0, 1101)) 131 | c = dllist([1, 2, 3, 4]) 132 | d = dllist([1, 2, 3, 5]) 133 | e = dllist([1, 0, 0, 0]) 134 | f = dllist([0, 0, 0, 0]) 135 | self.assertFalse(dllist() != dllist()) 136 | self.assertFalse(a != a) 137 | self.assertTrue(dllist() != a) 138 | self.assertTrue(a != dllist()) 139 | self.assertTrue(a != b) 140 | self.assertTrue(b != a) 141 | self.assertTrue(c != d) 142 | self.assertTrue(d != c) 143 | self.assertTrue(e != f) 144 | self.assertTrue(f != e) 145 | 146 | def test_lt(self): 147 | a = dllist(py23_xrange(0, 1100)) 148 | b = dllist(py23_xrange(0, 1101)) 149 | c = dllist([1, 2, 3, 4]) 150 | d = dllist([1, 2, 3, 5]) 151 | e = dllist([1, 0, 0, 0]) 152 | f = dllist([0, 0, 0, 0]) 153 | self.assertFalse(dllist() < dllist()) 154 | self.assertFalse(a < a) 155 | self.assertTrue(dllist() < a) 156 | self.assertFalse(a < dllist()) 157 | self.assertTrue(a < b) 158 | self.assertFalse(b < a) 159 | self.assertTrue(c < d) 160 | self.assertFalse(d < c) 161 | self.assertFalse(e < f) 162 | self.assertTrue(f < e) 163 | 164 | def test_gt(self): 165 | a = dllist(py23_xrange(0, 1100)) 166 | b = dllist(py23_xrange(0, 1101)) 167 | c = dllist([1, 2, 3, 4]) 168 | d = dllist([1, 2, 3, 5]) 169 | e = dllist([1, 0, 0, 0]) 170 | f = dllist([0, 0, 0, 0]) 171 | self.assertFalse(dllist() > dllist()) 172 | self.assertFalse(a > a) 173 | self.assertFalse(dllist() > a) 174 | self.assertTrue(a > dllist()) 175 | self.assertFalse(a > b) 176 | self.assertTrue(b > a) 177 | self.assertFalse(c > d) 178 | self.assertTrue(d > c) 179 | self.assertTrue(e > f) 180 | self.assertFalse(f > e) 181 | 182 | def test_le(self): 183 | a = dllist(py23_xrange(0, 1100)) 184 | b = dllist(py23_xrange(0, 1101)) 185 | c = dllist([1, 2, 3, 4]) 186 | d = dllist([1, 2, 3, 5]) 187 | e = dllist([1, 0, 0, 0]) 188 | f = dllist([0, 0, 0, 0]) 189 | self.assertTrue(dllist() <= dllist()) 190 | self.assertTrue(a <= a) 191 | self.assertTrue(dllist() <= a) 192 | self.assertFalse(a <= dllist()) 193 | self.assertTrue(a <= b) 194 | self.assertFalse(b <= a) 195 | self.assertTrue(c <= d) 196 | self.assertFalse(d <= c) 197 | self.assertFalse(e <= f) 198 | self.assertTrue(f <= e) 199 | 200 | def test_ge(self): 201 | a = dllist(py23_xrange(0, 1100)) 202 | b = dllist(py23_xrange(0, 1101)) 203 | c = dllist([1, 2, 3, 4]) 204 | d = dllist([1, 2, 3, 5]) 205 | e = dllist([1, 0, 0, 0]) 206 | f = dllist([0, 0, 0, 0]) 207 | self.assertTrue(dllist() >= dllist()) 208 | self.assertTrue(a >= a) 209 | self.assertFalse(dllist() >= a) 210 | self.assertTrue(a >= dllist()) 211 | self.assertFalse(a >= b) 212 | self.assertTrue(b >= a) 213 | self.assertFalse(c >= d) 214 | self.assertTrue(d >= c) 215 | self.assertTrue(e >= f) 216 | self.assertFalse(f >= e) 217 | 218 | def test_nodeat(self): 219 | ref = py23_range(0, 1024, 4) 220 | ll = dllist(ref) 221 | for idx in py23_xrange(len(ll)): 222 | self.assertTrue(isinstance(ll.nodeat(idx), dllistnode)) 223 | self.assertEqual(ll.nodeat(idx).value, ref[idx]) 224 | for idx in py23_xrange(len(ll)): 225 | self.assertTrue(isinstance(ll.nodeat(idx), dllistnode)) 226 | self.assertEqual(ll.nodeat(-idx - 1).value, ref[-idx - 1]) 227 | self.assertRaises(TypeError, ll.nodeat, None) 228 | self.assertRaises(TypeError, ll.nodeat, 'abc') 229 | self.assertRaises(IndexError, ll.nodeat, len(ref)) 230 | self.assertRaises(IndexError, ll.nodeat, -len(ref) - 1) 231 | 232 | def test_nodeat_empty(self): 233 | ll = dllist() 234 | self.assertRaises(TypeError, ll.nodeat, None) 235 | self.assertRaises(TypeError, ll.nodeat, 'abc') 236 | self.assertRaises(IndexError, ll.nodeat, 0) 237 | self.assertRaises(IndexError, ll.nodeat, -1) 238 | 239 | def test_iter(self): 240 | ref = py23_range(0, 1024, 4) 241 | ll = dllist(ref) 242 | idx = 0 243 | for val in ll: 244 | self.assertNotIsInstance(val, dllistnode) 245 | self.assertEqual(val, ref[idx]) 246 | idx += 1 247 | self.assertEqual(idx, len(ref)) 248 | 249 | def test_iter_on_iterator_returns_same_object(self): 250 | ll = dllist([0, 1, 2, 3]) 251 | first_iter = iter(ll) 252 | second_iter = iter(first_iter) 253 | self.assertIsNotNone(second_iter) 254 | self.assertIs(first_iter, second_iter) 255 | 256 | def test_iter_with_empty_list(self): 257 | ll = dllist() 258 | count = 0 259 | for val in ll: 260 | count += 1 261 | self.assertEqual(count, 0) 262 | 263 | def test_iter_with_appended_node(self): 264 | ll = dllist(['initial item']) 265 | 266 | appended_item_visited = False 267 | 268 | for x in ll: 269 | if x == 'initial item': 270 | ll.append('new item') 271 | elif x == 'new item': 272 | appended_item_visited = True 273 | 274 | self.assertTrue(appended_item_visited) 275 | 276 | def test_iter_with_removed_node(self): 277 | ll = dllist(['x', 'removed item']) 278 | 279 | for x in ll: 280 | self.assertNotEqual(x, 'removed item') 281 | ll.remove(ll.last) 282 | 283 | def test_itervalues(self): 284 | ref = py23_range(0, 1024, 4) 285 | ll = dllist(ref) 286 | idx = 0 287 | for val in ll.itervalues(): 288 | self.assertNotIsInstance(val, dllistnode) 289 | self.assertEqual(val, ref[idx]) 290 | idx += 1 291 | self.assertEqual(idx, len(ref)) 292 | 293 | def test_iter_on_itervalues_iterator_returns_same_object(self): 294 | ll = dllist([0, 1, 2, 3]) 295 | first_iter = ll.itervalues() 296 | second_iter = iter(first_iter) 297 | self.assertIsNotNone(second_iter) 298 | self.assertIs(first_iter, second_iter) 299 | 300 | def test_itervalues_with_empty_list(self): 301 | ll = dllist() 302 | count = 0 303 | for val in ll.itervalues(): 304 | count += 1 305 | self.assertEqual(count, 0) 306 | 307 | def test_itervalues_with_appended_node(self): 308 | ll = dllist(['initial item']) 309 | 310 | appended_item_visited = False 311 | 312 | for x in ll.itervalues(): 313 | if x == 'initial item': 314 | ll.append('new item') 315 | elif x == 'new item': 316 | appended_item_visited = True 317 | 318 | self.assertTrue(appended_item_visited) 319 | 320 | def test_itervalues_with_removed_node(self): 321 | ll = dllist(['x', 'removed item']) 322 | 323 | for x in ll.itervalues(): 324 | self.assertNotEqual(x, 'removed item') 325 | ll.remove(ll.last) 326 | 327 | def test_iternodes(self): 328 | ref = list(py23_range(0, 1024, 4)) 329 | ll = dllist(ref) 330 | idx = 0 331 | for node in ll.iternodes(): 332 | self.assertIsInstance(node, dllistnode) 333 | self.assertIs(node, ll.nodeat(idx)) 334 | idx += 1 335 | self.assertEqual(idx, len(ref)) 336 | 337 | def test_iter_on_iternodes_iterator_returns_same_object(self): 338 | ll = dllist([0, 1, 2, 3]) 339 | first_iter = ll.iternodes() 340 | second_iter = iter(first_iter) 341 | self.assertIsNotNone(second_iter) 342 | self.assertIs(first_iter, second_iter) 343 | 344 | def test_iternodes_with_empty_list(self): 345 | ll = dllist() 346 | count = 0 347 | for node in ll.iternodes(): 348 | count += 1 349 | self.assertEqual(count, 0) 350 | 351 | def test_iternodes_with_appended_node(self): 352 | ll = dllist(['initial item']) 353 | 354 | appended_node = None 355 | appended_node_visited = False 356 | 357 | for node in ll.iternodes(): 358 | if node == ll.first: 359 | appended_node = ll.append('new item') 360 | elif node is not None and node is appended_node: 361 | appended_node_visited = True 362 | 363 | self.assertTrue(appended_node_visited) 364 | 365 | def test_iternodes_with_removed_node(self): 366 | ll = dllist(['x', 'removed item']) 367 | removed_node = ll.last 368 | 369 | for node in ll.iternodes(): 370 | self.assertNotEqual(node, removed_node) 371 | ll.remove(removed_node) 372 | 373 | def test_reversed(self): 374 | ref = py23_range(0, 1024, 4) 375 | ll = dllist(ref) 376 | idx = len(ref) - 1 377 | for val in reversed(ll): 378 | self.assertFalse(isinstance(val, dllistnode)) 379 | self.assertEqual(val, ref[idx]) 380 | idx -= 1 381 | self.assertEqual(idx, -1) 382 | 383 | def test_reversed_empty(self): 384 | ll = dllist() 385 | count = 0 386 | for val in reversed(ll): 387 | count += 1 388 | self.assertEqual(count, 0) 389 | 390 | def test_insert_value(self): 391 | ll = dllist(py23_xrange(4)) 392 | ref = dllist([0, 1, 2, 3, 10]) 393 | prev = ll.nodeat(-1) 394 | arg_node = dllistnode(10) 395 | new_node = ll.insert(arg_node) 396 | self.assertNotEqual(new_node, arg_node) 397 | self.assertEqual(new_node.value, 10) 398 | self.assertEqual(new_node.prev, prev) 399 | self.assertEqual(new_node.next, None) 400 | self.assertEqual(prev.next, new_node) 401 | self.assertEqual(new_node, ll.last) 402 | self.assertEqual(ll, ref) 403 | 404 | def test_insert_value_before(self): 405 | ll = dllist(py23_xrange(4)) 406 | ref = dllist([0, 1, 10, 2, 3]) 407 | prev = ll.nodeat(1) 408 | next = ll.nodeat(2) 409 | arg_node = dllistnode(10) 410 | new_node = ll.insert(arg_node, ll.nodeat(2)) 411 | self.assertNotEqual(new_node, arg_node) 412 | self.assertEqual(new_node.value, 10) 413 | self.assertEqual(new_node.prev, prev) 414 | self.assertEqual(new_node.next, next) 415 | self.assertEqual(prev.next, new_node) 416 | self.assertEqual(next.prev, new_node) 417 | self.assertEqual(ll, ref) 418 | 419 | def test_insert_value_before_first(self): 420 | ll = dllist(py23_xrange(4)) 421 | ref = dllist([10, 0, 1, 2, 3]) 422 | next = ll.nodeat(0) 423 | arg_node = dllistnode(10) 424 | new_node = ll.insert(arg_node, ll.nodeat(0)) 425 | self.assertNotEqual(new_node, arg_node) 426 | self.assertEqual(new_node.value, 10) 427 | self.assertEqual(new_node.prev, None) 428 | self.assertEqual(new_node.next, next) 429 | self.assertEqual(next.prev, new_node) 430 | self.assertEqual(new_node, ll.first) 431 | self.assertEqual(ll, ref) 432 | 433 | def test_insert_invalid_ref(self): 434 | ll = dllist() 435 | self.assertRaises(TypeError, ll.insert, 10, 1) 436 | self.assertRaises(TypeError, ll.insert, 10, 'abc') 437 | self.assertRaises(TypeError, ll.insert, 10, []) 438 | self.assertRaises(ValueError, ll.insert, 10, dllistnode()) 439 | 440 | def test_insertbefore_without_ref_node(self): 441 | ll = dllist() 442 | self.assertRaises(TypeError, ll.insertbefore, 1234) 443 | 444 | def test_insertbefore_with_invalid_ref_node(self): 445 | ll = dllist() 446 | other_list = dllist(['node in other list']) 447 | self.assertRaises(TypeError, ll.insertbefore, 1234, None) 448 | self.assertRaises(TypeError, ll.insertbefore, 1234, 'not a dllist node') 449 | self.assertRaises(ValueError, ll.insertbefore, 1234, dllistnode()) 450 | self.assertRaises(ValueError, ll.insertbefore, 1234, other_list.first) 451 | 452 | def test_insertbefore_adds_item_in_correct_position(self): 453 | ll = dllist([0, 1, 2, 3, 4]) 454 | ref_node = ll.nodeat(2) 455 | ll.insertbefore(1234, ref_node) 456 | self.assertEqual(ll, dllist([0, 1, 1234, 2, 3, 4])) 457 | 458 | def test_insertbefore_returns_node_with_inserted_value(self): 459 | ll = dllist([0]) 460 | value = 'inserted value' 461 | new_node = ll.insertbefore(value, ll.first) 462 | self.assertIs(new_node.value, value) 463 | 464 | def test_insertbefore_extracts_value_from_inserted_node(self): 465 | ll = dllist([0]) 466 | free_node = dllistnode('free node') 467 | other_list = dllist(['node in other list']) 468 | new_node = ll.insertbefore(free_node, ll.first) 469 | self.assertIsNot(new_node, free_node) 470 | self.assertIs(new_node.value, free_node.value) 471 | new_node = ll.insertbefore(other_list.first, ll.first) 472 | self.assertIsNot(new_node, other_list.first) 473 | self.assertIs(new_node.value, other_list.first.value) 474 | 475 | def test_insertbefore_correctly_links_items(self): 476 | ll = dllist([0, 1, 2, 3]) 477 | prev_node = ll.nodeat(1) 478 | next_node = ll.nodeat(2) 479 | new_node = ll.insertbefore(1234, next_node) 480 | self.assertIs(new_node.prev, prev_node) 481 | self.assertIs(prev_node.next, new_node) 482 | self.assertIs(new_node.next, next_node) 483 | self.assertIs(next_node.prev, new_node) 484 | 485 | def test_insertbefore_before_first_item_makes_node_without_prev(self): 486 | ll = dllist([0]) 487 | new_node = ll.insertbefore(1234, ll.first) 488 | self.assertIs(new_node.prev, None) 489 | 490 | def test_insertbefore_before_first_item_updates_list_head(self): 491 | ll = dllist([0]) 492 | new_node = ll.insertbefore(1234, ll.first) 493 | self.assertIs(ll.first, new_node) 494 | 495 | def test_insertbefore_after_first_item_does_not_update_list_head(self): 496 | ll = dllist([0, 1]) 497 | original_head = ll.first 498 | ll.insertbefore(1234, ll.last) 499 | self.assertIs(ll.first, original_head) 500 | 501 | def test_insertbefore_does_not_update_list_tail(self): 502 | ll = dllist([0]) 503 | original_tail = ll.last 504 | ll.insertbefore(1234, ll.last) 505 | self.assertIs(ll.last, original_tail) 506 | 507 | def test_insertbefore_updates_list_length(self): 508 | ll = dllist([0]) 509 | self.assertEqual(len(ll), 1) 510 | ll.insertbefore(1234, ll.first) 511 | self.assertEqual(len(ll), 2) 512 | 513 | def test_insertafter_without_ref_node(self): 514 | ll = dllist() 515 | self.assertRaises(TypeError, ll.insertafter, 1234) 516 | 517 | def test_insertafter_with_invalid_ref_node(self): 518 | ll = dllist() 519 | other_list = dllist(['node in other list']) 520 | self.assertRaises(TypeError, ll.insertafter, 1234, None) 521 | self.assertRaises(TypeError, ll.insertafter, 1234, 'not a dllist node') 522 | self.assertRaises(ValueError, ll.insertafter, 1234, dllistnode()) 523 | self.assertRaises(ValueError, ll.insertafter, 1234, other_list.first) 524 | 525 | def test_insertafter_adds_item_in_correct_position(self): 526 | ll = dllist([0, 1, 2, 3, 4]) 527 | ref_node = ll.nodeat(2) 528 | ll.insertafter(1234, ref_node) 529 | self.assertEqual(ll, dllist([0, 1, 2, 1234, 3, 4])) 530 | 531 | def test_insertafter_returns_node_with_inserted_value(self): 532 | ll = dllist([0]) 533 | value = 'inserted value' 534 | new_node = ll.insertafter(value, ll.first) 535 | self.assertIs(new_node.value, value) 536 | 537 | def test_insertafter_extracts_value_from_inserted_node(self): 538 | ll = dllist([0]) 539 | free_node = dllistnode('free node') 540 | other_list = dllist(['node in other list']) 541 | new_node = ll.insertafter(free_node, ll.first) 542 | self.assertIsNot(new_node, free_node) 543 | self.assertIs(new_node.value, free_node.value) 544 | new_node = ll.insertafter(other_list.first, ll.first) 545 | self.assertIsNot(new_node, other_list.first) 546 | self.assertIs(new_node.value, other_list.first.value) 547 | 548 | def test_insertafter_correctly_links_items(self): 549 | ll = dllist([0, 1, 2, 3]) 550 | prev_node = ll.nodeat(1) 551 | next_node = ll.nodeat(2) 552 | new_node = ll.insertafter(1234, prev_node) 553 | self.assertIs(new_node.prev, prev_node) 554 | self.assertIs(prev_node.next, new_node) 555 | self.assertIs(new_node.next, next_node) 556 | self.assertIs(next_node.prev, new_node) 557 | 558 | def test_insertafter_after_last_item_makes_node_without_next(self): 559 | ll = dllist([0]) 560 | new_node = ll.insertafter(1234, ll.last) 561 | self.assertIs(new_node.next, None) 562 | 563 | def test_insertafter_after_last_item_updates_list_tail(self): 564 | ll = dllist([0]) 565 | new_node = ll.insertafter(1234, ll.last) 566 | self.assertIs(ll.last, new_node) 567 | 568 | def test_insertafter_before_last_item_does_not_update_list_tail(self): 569 | ll = dllist([0, 1]) 570 | original_tail = ll.last 571 | ll.insertafter(1234, ll.first) 572 | self.assertIs(ll.last, original_tail) 573 | 574 | def test_insertafter_does_not_update_list_head(self): 575 | ll = dllist([0]) 576 | original_head = ll.first 577 | ll.insertafter(1234, ll.first) 578 | self.assertIs(ll.first, original_head) 579 | 580 | def test_insertafter_updates_list_length(self): 581 | ll = dllist([0]) 582 | self.assertEqual(len(ll), 1) 583 | ll.insertafter(1234, ll.first) 584 | self.assertEqual(len(ll), 2) 585 | 586 | def test_insert_node(self): 587 | ll = dllist(py23_xrange(4)) 588 | ref = dllist([0, 1, 2, 3, 10]) 589 | prev = ll.nodeat(-1) 590 | arg_node = dllistnode(10) 591 | new_node = ll.insertnode(arg_node) 592 | self.assertEqual(new_node, arg_node) 593 | self.assertEqual(new_node.value, 10) 594 | self.assertEqual(new_node.prev, prev) 595 | self.assertEqual(new_node.next, None) 596 | self.assertEqual(prev.next, new_node) 597 | self.assertEqual(new_node, ll.last) 598 | self.assertEqual(ll, ref) 599 | 600 | def test_insert_node_value_before(self): 601 | ll = dllist(py23_xrange(4)) 602 | ref = dllist([0, 1, 10, 2, 3]) 603 | prev = ll.nodeat(1) 604 | next = ll.nodeat(2) 605 | arg_node = dllistnode(10) 606 | new_node = ll.insertnode(arg_node, ll.nodeat(2)) 607 | self.assertEqual(new_node, arg_node) 608 | self.assertEqual(new_node.value, 10) 609 | self.assertEqual(new_node.prev, prev) 610 | self.assertEqual(new_node.next, next) 611 | self.assertEqual(prev.next, new_node) 612 | self.assertEqual(next.prev, new_node) 613 | self.assertEqual(ll, ref) 614 | 615 | def test_insert_node_value_before_first(self): 616 | ll = dllist(py23_xrange(4)) 617 | ref = dllist([10, 0, 1, 2, 3]) 618 | next = ll.nodeat(0) 619 | arg_node = dllistnode(10) 620 | new_node = ll.insertnode(arg_node, ll.nodeat(0)) 621 | self.assertEqual(new_node, arg_node) 622 | self.assertEqual(new_node.value, 10) 623 | self.assertEqual(new_node.prev, None) 624 | self.assertEqual(new_node.next, next) 625 | self.assertEqual(next.prev, new_node) 626 | self.assertEqual(new_node, ll.first) 627 | self.assertEqual(ll, ref) 628 | 629 | def test_insert_node_with_bad_argument_type(self): 630 | ll = dllist([1234]) 631 | self.assertRaises( 632 | TypeError, ll.insertnode, 'non-node argument', ll.first) 633 | 634 | def test_insert_node_with_already_owned_node(self): 635 | ll = dllist([1234]) 636 | other_list = dllist([5678]) 637 | self.assertRaises( 638 | ValueError, ll.insertnode, other_list.first, ll.first) 639 | 640 | def test_insert_node_with_invalid_ref(self): 641 | ll = dllist() 642 | self.assertRaises(TypeError, ll.insertnode, dllistnode(10), 1) 643 | self.assertRaises(TypeError, ll.insertnode, dllistnode(10), 'abc') 644 | self.assertRaises(TypeError, ll.insertnode, dllistnode(10), []) 645 | self.assertRaises( 646 | ValueError, ll.insertnode, dllistnode(10), dllistnode()) 647 | 648 | def test_insert_node_refcount_update(self): 649 | ll = dllist([1234]) 650 | node = ll.insertnode(dllistnode(5678), ll.nodeat(0)) 651 | self.assertGreaterEqual(sys.getrefcount(node), 3) 652 | 653 | def test_insertnodebefore_with_invalid_type_of_inserted_node(self): 654 | ll = dllist([0]) 655 | self.assertRaises( 656 | TypeError, ll.insertnodebefore, None, ll.first) 657 | self.assertRaises( 658 | TypeError, ll.insertnodebefore, 'non-node argument', ll.first) 659 | self.assertRaises( 660 | TypeError, ll.insertnodebefore, sllistnode(1234), ll.first) 661 | 662 | def test_insertnodebefore_with_already_owned_inserted_node(self): 663 | ll = dllist([0]) 664 | other_list = dllist([0]) 665 | self.assertRaises(ValueError, ll.insertnodebefore, ll.first, ll.first) 666 | self.assertRaises( 667 | ValueError, ll.insertnodebefore, other_list.first, ll.first) 668 | 669 | def test_insertnodebefore_without_ref_node(self): 670 | ll = dllist([0]) 671 | self.assertRaises(TypeError, ll.insertnodebefore, dllistnode(1234)) 672 | 673 | def test_insertnodebefore_with_invalid_type_of_ref_node(self): 674 | ll = dllist([0]) 675 | self.assertRaises( 676 | TypeError, ll.insertnodebefore, dllistnode(1234), None) 677 | self.assertRaises( 678 | TypeError, ll.insertnodebefore, dllistnode(1234), 'not a dllist node') 679 | self.assertRaises( 680 | TypeError, ll.insertnodebefore, dllistnode(1234), sllistnode(1234)) 681 | 682 | def test_insertnodebefore_with_unowned_ref_node(self): 683 | ll = dllist([0]) 684 | self.assertRaises( 685 | ValueError, ll.insertnodebefore, dllistnode(1234), dllistnode()) 686 | 687 | def test_insertnodebefore_with_ref_node_from_different_list(self): 688 | ll = dllist([0]) 689 | other_list = dllist(['node in other list']) 690 | self.assertRaises( 691 | ValueError, ll.insertnodebefore, dllistnode(1234), other_list.first) 692 | 693 | def test_insertnodebefore_adds_node_in_correct_position(self): 694 | ll = dllist([0, 1, 2, 3, 4]) 695 | inserted_node = dllistnode(1234) 696 | ref_node = ll.nodeat(2) 697 | ll.insertnodebefore(inserted_node, ref_node) 698 | self.assertIs(ll.nodeat(2), inserted_node) 699 | self.assertIs(ll.nodeat(2).value, inserted_node.value) 700 | self.assertEqual(ll, dllist([0, 1, 1234, 2, 3, 4])) 701 | 702 | def test_insertnodebefore_returns_inserted_node(self): 703 | ll = dllist([0]) 704 | inserted_value = 'inserted value' 705 | inserted_node = dllistnode(inserted_value) 706 | returned_node = ll.insertnodebefore(inserted_node, ll.first) 707 | self.assertIs(returned_node, inserted_node) 708 | self.assertIs(returned_node.value, inserted_value) 709 | 710 | def test_insertnodebefore_correctly_links_items(self): 711 | ll = dllist([0, 1, 2, 3]) 712 | prev_node = ll.nodeat(1) 713 | next_node = ll.nodeat(2) 714 | inserted_node = dllistnode(1234) 715 | ll.insertnodebefore(inserted_node, next_node) 716 | self.assertIs(inserted_node.prev, prev_node) 717 | self.assertIs(prev_node.next, inserted_node) 718 | self.assertIs(inserted_node.next, next_node) 719 | self.assertIs(next_node.prev, inserted_node) 720 | 721 | def test_insertnodebefore_sets_prev_in_inserted_node_to_none_if_it_becomes_head(self): 722 | ll = dllist([0]) 723 | inserted_node = dllistnode(1234) 724 | ll.insertnodebefore(inserted_node, ll.first) 725 | self.assertIs(inserted_node.prev, None) 726 | 727 | def test_insertnodebefore_updates_list_head_if_inserted_node_becomes_head(self): 728 | ll = dllist([0]) 729 | inserted_node = dllistnode(1234) 730 | ll.insertnodebefore(inserted_node, ll.first) 731 | self.assertIs(ll.first, inserted_node) 732 | 733 | def test_insertnodebefore_after_first_item_does_not_update_list_head(self): 734 | ll = dllist([0, 1]) 735 | original_head = ll.first 736 | inserted_node = dllistnode(1234) 737 | ll.insertnodebefore(inserted_node, ll.last) 738 | self.assertIs(ll.first, original_head) 739 | 740 | def test_insertnodebefore_does_not_update_list_tail(self): 741 | ll = dllist([0]) 742 | original_tail = ll.last 743 | inserted_node = dllistnode(1234) 744 | ll.insertnodebefore(inserted_node, ll.last) 745 | self.assertIs(ll.last, original_tail) 746 | 747 | def test_insertnodebefore_updates_list_length(self): 748 | ll = dllist([0]) 749 | self.assertEqual(len(ll), 1) 750 | ll.insertnodebefore(dllistnode(1234), ll.first) 751 | self.assertEqual(len(ll), 2) 752 | 753 | def test_insertnodeafter_with_invalid_type_of_inserted_node(self): 754 | ll = dllist([0]) 755 | self.assertRaises( 756 | TypeError, ll.insertnodeafter, None, ll.first) 757 | self.assertRaises( 758 | TypeError, ll.insertnodeafter, 'non-node argument', ll.first) 759 | self.assertRaises( 760 | TypeError, ll.insertnodeafter, sllistnode(1234), ll.first) 761 | 762 | def test_insertnodeafter_with_already_owned_inserted_node(self): 763 | ll = dllist([0]) 764 | other_list = dllist([0]) 765 | self.assertRaises(ValueError, ll.insertnodeafter, ll.first, ll.first) 766 | self.assertRaises( 767 | ValueError, ll.insertnodeafter, other_list.first, ll.first) 768 | 769 | def test_insertnodeafter_without_ref_node(self): 770 | ll = dllist([0]) 771 | self.assertRaises(TypeError, ll.insertnodeafter, dllistnode(1234)) 772 | 773 | def test_insertnodeafter_with_invalid_type_of_ref_node(self): 774 | ll = dllist([0]) 775 | self.assertRaises(TypeError, ll.insertnodeafter, dllistnode(1234), None) 776 | self.assertRaises( 777 | TypeError, ll.insertnodeafter, dllistnode(1234), 'not a dllist node') 778 | self.assertRaises( 779 | TypeError, ll.insertnodeafter, dllistnode(1234), sllistnode(1234)) 780 | 781 | def test_insertnodeafter_with_unowned_ref_node(self): 782 | ll = dllist([0]) 783 | self.assertRaises( 784 | ValueError, ll.insertnodeafter, dllistnode(1234), dllistnode()) 785 | 786 | def test_insertnodeafter_with_ref_node_from_different_list(self): 787 | ll = dllist([0]) 788 | other_list = dllist(['node in other list']) 789 | self.assertRaises( 790 | ValueError, ll.insertnodeafter, dllistnode(1234), other_list.first) 791 | 792 | def test_insertnodeafter_adds_node_in_correct_position(self): 793 | ll = dllist([0, 1, 2, 3, 4]) 794 | inserted_node = dllistnode(1234) 795 | ref_node = ll.nodeat(2) 796 | ll.insertnodeafter(inserted_node, ref_node) 797 | self.assertIs(ll.nodeat(3), inserted_node) 798 | self.assertIs(ll.nodeat(3).value, inserted_node.value) 799 | self.assertEqual(ll, dllist([0, 1, 2, 1234, 3, 4])) 800 | 801 | def test_insertnodeafter_returns_inserted_node(self): 802 | ll = dllist([0]) 803 | inserted_value = 'inserted value' 804 | inserted_node = dllistnode(inserted_value) 805 | returned_node = ll.insertnodeafter(inserted_node, ll.first) 806 | self.assertIs(returned_node, inserted_node) 807 | self.assertIs(returned_node.value, inserted_value) 808 | 809 | def test_insertnodeafter_correctly_links_items(self): 810 | ll = dllist([0, 1, 2, 3]) 811 | prev_node = ll.nodeat(1) 812 | next_node = ll.nodeat(2) 813 | inserted_node = dllistnode(1234) 814 | ll.insertnodeafter(inserted_node, prev_node) 815 | self.assertIs(inserted_node.prev, prev_node) 816 | self.assertIs(prev_node.next, inserted_node) 817 | self.assertIs(inserted_node.next, next_node) 818 | self.assertIs(next_node.prev, inserted_node) 819 | 820 | def test_insertnodeafter_sets_next_in_inserted_node_to_none_if_it_becomes_tail(self): 821 | ll = dllist([0]) 822 | inserted_node = dllistnode(1234) 823 | ll.insertnodeafter(inserted_node, ll.last) 824 | self.assertIs(inserted_node.next, None) 825 | 826 | def test_insertnodeafter_updates_list_tail_if_inserted_node_becomes_tail(self): 827 | ll = dllist([0]) 828 | inserted_node = dllistnode(1234) 829 | ll.insertnodeafter(inserted_node, ll.last) 830 | self.assertIs(ll.last, inserted_node) 831 | 832 | def test_insertnodeafter_before_last_item_does_not_update_list_tail(self): 833 | ll = dllist([0, 1]) 834 | original_tail = ll.last 835 | inserted_node = dllistnode(1234) 836 | ll.insertnodeafter(inserted_node, ll.first) 837 | self.assertIs(ll.last, original_tail) 838 | 839 | def test_insertnodeafter_does_not_update_list_head(self): 840 | ll = dllist([0]) 841 | original_head = ll.first 842 | inserted_node = dllistnode(1234) 843 | ll.insertnodeafter(inserted_node, ll.first) 844 | self.assertIs(ll.first, original_head) 845 | 846 | def test_insertnodeafter_updates_list_length(self): 847 | ll = dllist([0]) 848 | self.assertEqual(len(ll), 1) 849 | ll.insertnodeafter(dllistnode(1234), ll.first) 850 | self.assertEqual(len(ll), 2) 851 | 852 | def test_append(self): 853 | ll = dllist(py23_xrange(4)) 854 | ref = dllist([0, 1, 2, 3, 10]) 855 | prev = ll.nodeat(-1) 856 | arg_node = dllistnode(10) 857 | new_node = ll.append(arg_node) 858 | self.assertNotEqual(new_node, arg_node) 859 | self.assertEqual(new_node.value, 10) 860 | self.assertEqual(new_node.prev, prev) 861 | self.assertEqual(new_node.next, None) 862 | self.assertEqual(prev.next, new_node) 863 | self.assertEqual(ll.last, new_node) 864 | self.assertEqual(ll, ref) 865 | 866 | def test_appendleft(self): 867 | ll = dllist(py23_xrange(4)) 868 | ref = dllist([10, 0, 1, 2, 3]) 869 | next = ll.nodeat(0) 870 | arg_node = dllistnode(10) 871 | new_node = ll.appendleft(arg_node) 872 | self.assertNotEqual(new_node, arg_node) 873 | self.assertEqual(new_node.value, 10) 874 | self.assertEqual(new_node.prev, None) 875 | self.assertEqual(new_node.next, next) 876 | self.assertEqual(next.prev, new_node) 877 | self.assertEqual(ll.first, new_node) 878 | self.assertEqual(ll, ref) 879 | 880 | def test_appendright(self): 881 | ll = dllist(py23_xrange(4)) 882 | ref = dllist([0, 1, 2, 3, 10]) 883 | prev = ll.nodeat(-1) 884 | arg_node = dllistnode(10) 885 | new_node = ll.appendright(arg_node) 886 | self.assertNotEqual(new_node, arg_node) 887 | self.assertEqual(new_node.value, 10) 888 | self.assertEqual(new_node.prev, prev) 889 | self.assertEqual(new_node.next, None) 890 | self.assertEqual(prev.next, new_node) 891 | self.assertEqual(ll.last, new_node) 892 | self.assertEqual(ll, ref) 893 | 894 | def test_appendnode(self): 895 | ll = dllist([1, 2, 3, 4]) 896 | node = dllistnode(5) 897 | ll.appendnode(node) 898 | self.assertEqual([1, 2, 3, 4, 5], list(ll)) 899 | self.assertIs(node, ll.last) 900 | 901 | def test_appendnode_with_bad_argument_type(self): 902 | ll = dllist() 903 | self.assertRaises(TypeError, ll.appendnode, 'non-node argument') 904 | 905 | def test_appendnode_with_already_owned_node(self): 906 | ll = dllist() 907 | other_list = dllist([1234]) 908 | self.assertRaises(ValueError, ll.appendnode, other_list.first) 909 | 910 | def test_appendnode_refcount_update(self): 911 | ll = dllist() 912 | node = ll.appendnode(dllistnode(1234)) 913 | self.assertGreaterEqual(sys.getrefcount(node), 3) 914 | 915 | def test_extend(self): 916 | a_ref = py23_range(0, 1024, 4) 917 | b_ref = py23_range(8092, 8092 + 1024, 4) 918 | b = dllist(b_ref) 919 | ab_ref = dllist(a_ref + b_ref) 920 | a = dllist(a_ref) 921 | a.extend(b) 922 | self.assertEqual(a, ab_ref) 923 | self.assertEqual(len(a), len(ab_ref)) 924 | a = dllist(a_ref) 925 | a.extend(b_ref) 926 | self.assertEqual(a, ab_ref) 927 | self.assertEqual(len(a), len(ab_ref)) 928 | a = dllist(a_ref) 929 | a.extend(a) 930 | self.assertEqual(a, dllist(a_ref + a_ref)) 931 | self.assertEqual(len(a), len(a_ref) * 2) 932 | 933 | def test_extend_empty(self): 934 | filled_ref = py23_range(0, 1024, 4) 935 | filled = dllist(filled_ref) 936 | empty = dllist() 937 | empty.extend(empty) 938 | self.assertEqual(empty, dllist([] + [])) 939 | self.assertEqual(len(empty), 0) 940 | empty = dllist() 941 | empty.extend(filled) 942 | self.assertEqual(empty, dllist([] + filled_ref)) 943 | self.assertEqual(len(empty), len(filled_ref)) 944 | empty = dllist() 945 | filled.extend(empty) 946 | self.assertEqual(filled, dllist(filled_ref + [])) 947 | self.assertEqual(len(filled), len(filled_ref)) 948 | 949 | def test_extendleft(self): 950 | a_ref = py23_range(0, 1024, 4) 951 | b_ref = py23_range(8092, 8092 + 1024, 4) 952 | b = dllist(b_ref) 953 | ab_ref = dllist(list(reversed(b_ref)) + a_ref) 954 | a = dllist(a_ref) 955 | a.extendleft(b) 956 | self.assertEqual(a, ab_ref) 957 | self.assertEqual(len(a), len(ab_ref)) 958 | a = dllist(a_ref) 959 | a.extendleft(b_ref) 960 | self.assertEqual(a, ab_ref) 961 | self.assertEqual(len(a), len(ab_ref)) 962 | a = dllist(a_ref) 963 | a.extendleft(a) 964 | self.assertEqual(a, dllist(list(reversed(a_ref)) + a_ref)) 965 | self.assertEqual(len(a), len(a_ref) * 2) 966 | 967 | def test_extendleft_empty(self): 968 | filled_ref = py23_range(0, 1024, 4) 969 | filled = dllist(filled_ref) 970 | empty = dllist() 971 | empty.extendleft(empty) 972 | self.assertEqual(empty, dllist([] + [])) 973 | self.assertEqual(len(empty), 0) 974 | empty = dllist() 975 | empty.extendleft(filled) 976 | self.assertEqual(empty, dllist(list(reversed(filled_ref)) + [])) 977 | self.assertEqual(len(empty), len(filled_ref)) 978 | empty = dllist() 979 | filled.extendleft(empty) 980 | self.assertEqual(filled, dllist(list(reversed([])) + filled_ref)) 981 | self.assertEqual(len(filled), len(filled_ref)) 982 | 983 | def test_extendright(self): 984 | a_ref = py23_range(0, 1024, 4) 985 | b_ref = py23_range(8092, 8092 + 1024, 4) 986 | b = dllist(b_ref) 987 | ab_ref = dllist(a_ref + b_ref) 988 | a = dllist(a_ref) 989 | a.extendright(b) 990 | self.assertEqual(a, ab_ref) 991 | self.assertEqual(len(a), len(ab_ref)) 992 | a = dllist(a_ref) 993 | a.extendright(b_ref) 994 | self.assertEqual(a, ab_ref) 995 | self.assertEqual(len(a), len(ab_ref)) 996 | a = dllist(a_ref) 997 | a.extendright(a) 998 | self.assertEqual(a, dllist(a_ref + a_ref)) 999 | self.assertEqual(len(a), len(a_ref) * 2) 1000 | 1001 | def test_extendright_empty(self): 1002 | filled_ref = py23_range(0, 1024, 4) 1003 | filled = dllist(filled_ref) 1004 | empty = dllist() 1005 | empty.extendright(empty) 1006 | self.assertEqual(empty, dllist([] + [])) 1007 | self.assertEqual(len(empty), 0) 1008 | empty = dllist() 1009 | empty.extendright(filled) 1010 | self.assertEqual(empty, dllist([] + filled_ref)) 1011 | self.assertEqual(len(empty), len(filled_ref)) 1012 | empty = dllist() 1013 | filled.extendright(empty) 1014 | self.assertEqual(filled, dllist(filled_ref + [])) 1015 | self.assertEqual(len(filled), len(filled_ref)) 1016 | 1017 | def test_clear_empty(self): 1018 | empty_list = dllist() 1019 | empty_list.clear() 1020 | self.assertEqual(empty_list.first, None) 1021 | self.assertEqual(empty_list.last, None) 1022 | self.assertEqual(empty_list.size, 0) 1023 | self.assertEqual(list(empty_list), []) 1024 | 1025 | def test_clear(self): 1026 | ll = dllist(py23_xrange(0, 1024, 4)) 1027 | del_node = ll.nodeat(4) 1028 | ll.clear() 1029 | self.assertEqual(ll.first, None) 1030 | self.assertEqual(ll.last, None) 1031 | self.assertEqual(ll.size, 0) 1032 | self.assertEqual(list(ll), []) 1033 | self.assertEqual(del_node.prev, None) 1034 | self.assertEqual(del_node.next, None) 1035 | 1036 | def test_pop(self): 1037 | ref = py23_range(0, 1024, 4) 1038 | ll = dllist(ref) 1039 | result = ll.pop() 1040 | self.assertEqual(result, ref[-1]) 1041 | self.assertEqual(len(ll), len(ref) - 1) 1042 | self.assertEqual(ll.size, len(ref) - 1) 1043 | self.assertEqual(ll.last.value, ref[-2]) 1044 | self.assertEqual(list(ll), ref[:-1]) 1045 | 1046 | def test_node_after_pop(self): 1047 | ll = dllist([1, 2]) 1048 | del_node = ll.last 1049 | ll.pop() 1050 | self.assertIs(del_node.prev, None) 1051 | self.assertIs(del_node.next, None) 1052 | self.assertIs(del_node.owner, None) 1053 | 1054 | def test_popleft(self): 1055 | ref = py23_range(0, 1024, 4) 1056 | ll = dllist(ref) 1057 | result = ll.popleft() 1058 | self.assertEqual(result, ref[0]) 1059 | self.assertEqual(len(ll), len(ref) - 1) 1060 | self.assertEqual(ll.size, len(ref) - 1) 1061 | self.assertEqual(ll.first.value, ref[1]) 1062 | self.assertEqual(list(ll), ref[1:]) 1063 | 1064 | def test_node_after_popleft(self): 1065 | ll = dllist([1, 2]) 1066 | del_node = ll.first 1067 | ll.popleft() 1068 | self.assertIs(del_node.prev, None) 1069 | self.assertIs(del_node.next, None) 1070 | self.assertIs(del_node.owner, None) 1071 | 1072 | def test_popright(self): 1073 | ref = py23_range(0, 1024, 4) 1074 | ll = dllist(ref) 1075 | result = ll.popright() 1076 | self.assertEqual(result, ref[-1]) 1077 | self.assertEqual(len(ll), len(ref) - 1) 1078 | self.assertEqual(ll.size, len(ref) - 1) 1079 | self.assertEqual(ll.last.value, ref[-2]) 1080 | self.assertEqual(list(ll), ref[:-1]) 1081 | 1082 | def test_node_after_popright(self): 1083 | ll = dllist([1, 2]) 1084 | del_node = ll.last 1085 | ll.popright() 1086 | self.assertIs(del_node.prev, None) 1087 | self.assertIs(del_node.next, None) 1088 | self.assertIs(del_node.owner, None) 1089 | 1090 | def test_pop_from_empty_list(self): 1091 | ll = dllist() 1092 | self.assertRaises(ValueError, ll.pop) 1093 | self.assertRaises(ValueError, ll.popleft) 1094 | self.assertRaises(ValueError, ll.popright) 1095 | 1096 | def test_remove(self): 1097 | ref = py23_range(0, 1024, 4) 1098 | ll = dllist(ref) 1099 | prev_node = ll.nodeat(3) 1100 | del_node = ll.nodeat(4) 1101 | next_node = ll.nodeat(5) 1102 | result = ll.remove(del_node) 1103 | ref_result = ref[4] 1104 | del ref[4] 1105 | self.assertEqual(list(ll), ref) 1106 | self.assertEqual(result, ref_result) 1107 | self.assertEqual(len(ll), len(ref)) 1108 | self.assertEqual(ll.size, len(ref)) 1109 | self.assertEqual(prev_node.next, next_node) 1110 | self.assertEqual(next_node.prev, prev_node) 1111 | 1112 | def test_remove_after_remove(self): 1113 | ll = dllist([1, 2, 3]) 1114 | del_node = ll.nodeat(1) 1115 | ll.remove(del_node) 1116 | self.assertIs(del_node.prev, None) 1117 | self.assertIs(del_node.next, None) 1118 | self.assertIs(del_node.owner, None) 1119 | 1120 | def test_remove_from_empty_list(self): 1121 | ll = dllist() 1122 | self.assertRaises(ValueError, ll.remove, dllistnode()) 1123 | 1124 | def test_remove_invalid_node(self): 1125 | ll = dllist([1, 2, 3, 4]) 1126 | self.assertRaises(ValueError, ll.remove, dllistnode()) 1127 | self.assertEqual(len(ll), 4) 1128 | 1129 | def test_remove_already_deleted_node(self): 1130 | ll = dllist([1, 2, 3, 4]) 1131 | node = ll.nodeat(2) 1132 | ll.remove(node) 1133 | self.assertRaises(ValueError, ll.remove, node) 1134 | 1135 | def test_rotate_left(self): 1136 | for n in py23_xrange(128): 1137 | ref = py23_range(32) 1138 | split = n % len(ref) 1139 | ref_result = ref[split:] + ref[:split] 1140 | ll = dllist(ref) 1141 | new_first = ll.nodeat(split) 1142 | new_last = ll.nodeat(split - 1) 1143 | # touch future middle element to initialize cache 1144 | cached_idx = (len(ll) // 2 + n) % len(ll) 1145 | ll[cached_idx] 1146 | ll.rotate(-n) 1147 | self.assertEqual(list(ll), ref_result) 1148 | self.assertEqual(ll.first, new_first) 1149 | self.assertEqual(ll.last, new_last) 1150 | self.assertEqual(ll.size, len(ref)) 1151 | self.assertEqual(ll.first.prev, None) 1152 | self.assertEqual(ll.first.next.prev, ll.first) 1153 | self.assertEqual(ll.last.next, None) 1154 | self.assertEqual(ll.last.prev.next, ll.last) 1155 | # check if cached index is updated correctly 1156 | self.assertEqual(ll[len(ll) // 2], ref_result[len(ref_result) // 2]) 1157 | 1158 | def test_rotate_right(self): 1159 | for n in py23_xrange(128): 1160 | ref = py23_range(32) 1161 | split = n % len(ref) 1162 | ref_result = ref[-split:] + ref[:-split] 1163 | ll = dllist(ref) 1164 | new_first = ll.nodeat(-split) 1165 | last_idx = -split - 1 1166 | new_last = ll.nodeat(last_idx) 1167 | # touch future middle element to initialize cache 1168 | cached_idx = len(ll) - (len(ll) // 2 + n) % len(ll) - 1 1169 | ll[cached_idx] 1170 | ll.rotate(n) 1171 | self.assertEqual(list(ll), ref_result) 1172 | self.assertEqual(ll.first, new_first) 1173 | self.assertEqual(ll.last, new_last) 1174 | self.assertEqual(ll.size, len(ref)) 1175 | self.assertEqual(ll.first.prev, None) 1176 | self.assertEqual(ll.first.next.prev, ll.first) 1177 | self.assertEqual(ll.last.next, None) 1178 | self.assertEqual(ll.last.prev.next, ll.last) 1179 | # check if cached index is updated correctly 1180 | self.assertEqual(ll[len(ll) // 2], ref_result[len(ref_result) // 2]) 1181 | 1182 | def test_rotate_left_empty(self): 1183 | for n in py23_xrange(4): 1184 | ll = dllist() 1185 | ll.rotate(-n) 1186 | self.assertEqual(ll.first, None) 1187 | self.assertEqual(ll.last, None) 1188 | self.assertEqual(ll.size, 0) 1189 | 1190 | def test_rotate_right_empty(self): 1191 | for n in py23_xrange(4): 1192 | ll = dllist() 1193 | ll.rotate(n) 1194 | self.assertEqual(ll.first, None) 1195 | self.assertEqual(ll.last, None) 1196 | self.assertEqual(ll.size, 0) 1197 | 1198 | def test_getitem(self): 1199 | ref = py23_range(0, 1024, 4) 1200 | ll = dllist(ref) 1201 | for idx in py23_xrange(len(ll)): 1202 | self.assertFalse(isinstance(ll[idx], dllistnode)) 1203 | self.assertEqual(ll[idx], ref[idx]) 1204 | for idx in py23_xrange(len(ll)): 1205 | self.assertFalse(isinstance(ll[idx], dllistnode)) 1206 | self.assertEqual(ll[-idx - 1], ref[-idx - 1]) 1207 | self.assertRaises(TypeError, ll.__getitem__, None) 1208 | self.assertRaises(TypeError, ll.__getitem__, 'abc') 1209 | self.assertRaises(IndexError, ll.__getitem__, len(ref)) 1210 | self.assertRaises(IndexError, ll.__getitem__, -len(ref) - 1) 1211 | 1212 | def test_getitem_empty(self): 1213 | ll = dllist() 1214 | self.assertRaises(TypeError, ll.__getitem__, None) 1215 | self.assertRaises(TypeError, ll.__getitem__, 'abc') 1216 | self.assertRaises(IndexError, ll.__getitem__, 0) 1217 | self.assertRaises(IndexError, ll.__getitem__, -1) 1218 | 1219 | def test_del(self): 1220 | ref = py23_range(0, 1024, 4) 1221 | ll = dllist(ref) 1222 | del ll[0] 1223 | del ref[0] 1224 | self.assertEqual(list(ll), ref) 1225 | del ll[len(ll) - 1] 1226 | del ref[len(ref) - 1] 1227 | self.assertEqual(list(ll), ref) 1228 | del ll[(len(ll) - 1) // 2] 1229 | del ref[(len(ref) - 1) // 2] 1230 | self.assertEqual(list(ll), ref) 1231 | 1232 | def del_item(idx): 1233 | del ll[idx] 1234 | self.assertRaises(IndexError, del_item, len(ll)) 1235 | 1236 | for i in py23_xrange(len(ll)): 1237 | del ll[0] 1238 | self.assertEqual(len(ll), 0) 1239 | 1240 | def test_node_after_del(self): 1241 | ll = dllist([1, 2, 3]) 1242 | del_node = ll.nodeat(1) 1243 | del ll[1] 1244 | self.assertIs(del_node.prev, None) 1245 | self.assertIs(del_node.next, None) 1246 | self.assertIs(del_node.owner, None) 1247 | 1248 | def test_concat(self): 1249 | a_ref = py23_range(0, 1024, 4) 1250 | a = dllist(a_ref) 1251 | b_ref = py23_range(8092, 8092 + 1024, 4) 1252 | b = dllist(b_ref) 1253 | ab_ref = dllist(a_ref + b_ref) 1254 | c = a + b 1255 | self.assertEqual(c, ab_ref) 1256 | self.assertEqual(len(c), len(ab_ref)) 1257 | c = a + b_ref 1258 | self.assertEqual(c, ab_ref) 1259 | self.assertEqual(len(c), len(a_ref) * 2) 1260 | 1261 | def test_concat_empty(self): 1262 | empty = dllist() 1263 | filled_ref = py23_range(0, 1024, 4) 1264 | filled = dllist(filled_ref) 1265 | res = empty + empty 1266 | self.assertEqual(res, dllist([] + [])) 1267 | self.assertEqual(len(res), 0) 1268 | res = empty + filled 1269 | self.assertEqual(res, dllist([] + filled_ref)) 1270 | self.assertEqual(len(res), len(filled_ref)) 1271 | res = filled + empty 1272 | self.assertEqual(res, dllist(filled_ref + [])) 1273 | self.assertEqual(len(res), len(filled_ref)) 1274 | 1275 | def test_concat_inplace(self): 1276 | a_ref = py23_range(0, 1024, 4) 1277 | b_ref = py23_range(8092, 8092 + 1024, 4) 1278 | b = dllist(b_ref) 1279 | ab_ref = dllist(a_ref + b_ref) 1280 | a = dllist(a_ref) 1281 | a += b 1282 | self.assertEqual(a, ab_ref) 1283 | self.assertEqual(len(a), len(ab_ref)) 1284 | a = dllist(a_ref) 1285 | a += b_ref 1286 | self.assertEqual(a, ab_ref) 1287 | self.assertEqual(len(a), len(ab_ref)) 1288 | a = dllist(a_ref) 1289 | a += a 1290 | self.assertEqual(a, dllist(a_ref + a_ref)) 1291 | self.assertEqual(len(a), len(a_ref) * 2) 1292 | 1293 | def test_concat_inplace_empty(self): 1294 | filled_ref = py23_range(0, 1024, 4) 1295 | filled = dllist(filled_ref) 1296 | empty = dllist() 1297 | empty += empty 1298 | self.assertEqual(empty, dllist([] + [])) 1299 | self.assertEqual(len(empty), 0) 1300 | empty = dllist() 1301 | empty += filled 1302 | self.assertEqual(empty, dllist([] + filled_ref)) 1303 | self.assertEqual(len(empty), len(filled_ref)) 1304 | empty = dllist() 1305 | filled += empty 1306 | self.assertEqual(filled, dllist(filled_ref + [])) 1307 | self.assertEqual(len(filled), len(filled_ref)) 1308 | 1309 | def test_repeat(self): 1310 | ref = py23_range(0, 1024, 4) 1311 | ll = dllist(ref) 1312 | self.assertEqual(ll * 4, dllist(ref * 4)) 1313 | 1314 | def test_repeat_empty(self): 1315 | ll = dllist() 1316 | self.assertEqual(ll * 4, dllist([] * 4)) 1317 | 1318 | def test_repeat_inplace(self): 1319 | ref = py23_range(0, 1024, 4) 1320 | ll = dllist(ref) 1321 | ll *= 4 1322 | self.assertEqual(ll, dllist(ref * 4)) 1323 | 1324 | def test_repeat_inplace_empty(self): 1325 | ll = dllist() 1326 | ll *= 4 1327 | self.assertEqual(ll, dllist([] * 4)) 1328 | 1329 | def test_list_readonly_attributes(self): 1330 | if sys.hexversion >= 0x03000000: 1331 | expected_error = AttributeError 1332 | else: 1333 | expected_error = TypeError 1334 | 1335 | ll = dllist(py23_range(4)) 1336 | self.assertRaises(expected_error, setattr, ll, 'first', None) 1337 | self.assertRaises(expected_error, setattr, ll, 'last', None) 1338 | self.assertRaises(expected_error, setattr, ll, 'size', None) 1339 | 1340 | def test_node_readonly_attributes(self): 1341 | if sys.hexversion >= 0x03000000: 1342 | expected_error = AttributeError 1343 | else: 1344 | expected_error = TypeError 1345 | 1346 | ll = dllistnode() 1347 | self.assertRaises(expected_error, setattr, ll, 'prev', None) 1348 | self.assertRaises(expected_error, setattr, ll, 'next', None) 1349 | 1350 | def test_node_owner(self): 1351 | ll = dllist([1234]) 1352 | owner_ref = ll.first.owner 1353 | self.assertIsInstance(owner_ref, weakref.ref) 1354 | self.assertIs(owner_ref(), ll) 1355 | del ll 1356 | self.assertIsNone(owner_ref()) 1357 | 1358 | def test_list_hash(self): 1359 | self.assertEqual(hash(dllist()), hash(dllist())) 1360 | self.assertEqual(hash(dllist(py23_range(0, 1024, 4))), 1361 | hash(dllist(py23_range(0, 1024, 4)))) 1362 | self.assertEqual(hash(dllist([0, 2])), hash(dllist([0.0, 2.0]))) 1363 | self.assertNotEqual(hash(dllist([1, 2])), hash(dllist([2, 1]))) 1364 | 1365 | def test_list_can_be_subclassed(self): 1366 | class DerivedList(dllist): 1367 | pass 1368 | 1369 | def test_list_node_can_be_subclassed(self): 1370 | class DerivedNode(dllistnode): 1371 | pass 1372 | 1373 | def test_cyclic_list_destruction_does_not_release_extra_None_refs(self): 1374 | def create_and_free_lists(): 1375 | for _ in range(10): 1376 | ll = dllist() 1377 | n = dllistnode(ll) 1378 | ll.append(n) 1379 | del ll 1380 | del n 1381 | 1382 | self.assertStableRefCount(create_and_free_lists, None) 1383 | 1384 | def test_cyclic_node_destruction_does_not_release_extra_None_refs(self): 1385 | def create_and_free_lists(): 1386 | for _ in range(10): 1387 | ll = self.make_recursive_node_list() 1388 | del ll 1389 | 1390 | self.assertStableRefCount(create_and_free_lists, None) 1391 | -------------------------------------------------------------------------------- /tests/llist_test_case.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import gc 4 | import sys 5 | import unittest 6 | 7 | 8 | class LListTestCase(unittest.TestCase): 9 | 10 | def assertStableRefCount(self, callable, object): 11 | gc.collect() 12 | original_ref_count = sys.getrefcount(None) 13 | 14 | callable() 15 | 16 | gc.collect() 17 | new_ref_count = sys.getrefcount(None) 18 | self.assertEqual(new_ref_count, original_ref_count) 19 | -------------------------------------------------------------------------------- /tests/py23_utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | if sys.hexversion >= 0x03000000: 5 | # python 3 compatibility wrappers 6 | 7 | def py23_xrange(*args): 8 | return range(*args) 9 | 10 | def py23_range(*args): 11 | return list(range(*args)) 12 | 13 | def py23_cmp(a, b): 14 | if a == b: 15 | return 0 16 | elif a < b: 17 | return -1 18 | else: 19 | return 1 20 | 21 | else: 22 | # python 2 compatibility wrappers 23 | 24 | def py23_xrange(*args): 25 | return xrange(*args) # noqa: F821 26 | 27 | def py23_range(*args): 28 | return range(*args) 29 | 30 | def py23_cmp(*args): 31 | return cmp(*args) # noqa: F821 32 | -------------------------------------------------------------------------------- /tests/sllist_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | import weakref 5 | 6 | from llist_test_case import LListTestCase 7 | from py23_utils import py23_cmp, py23_range, py23_xrange 8 | 9 | from llist import sllist, sllistnode 10 | 11 | 12 | class testsllist(LListTestCase): 13 | 14 | def test_init_empty(self): 15 | ll = sllist() 16 | self.assertEqual(len(ll), 0) 17 | self.assertEqual(ll.size, 0) 18 | self.assertEqual(list(ll), []) 19 | 20 | def test_init_with_sequence(self): 21 | ref = py23_range(0, 1024, 4) 22 | ll = sllist(ref) 23 | self.assertEqual(len(ll), len(ref)) 24 | self.assertEqual(ll.size, len(ref)) 25 | self.assertEqual(list(ll), ref) 26 | 27 | def test_init_with_non_sequence(self): 28 | self.assertRaises(TypeError, sllist, 1) 29 | self.assertRaises(TypeError, sllist, 1.5) 30 | 31 | def test_str(self): 32 | a = sllist([]) 33 | self.assertEqual(str(a), 'sllist()') 34 | b = sllist([None, 1, 'abc']) 35 | self.assertEqual(str(b), 'sllist([None, 1, abc])') 36 | 37 | def test_repr(self): 38 | a = sllist([]) 39 | self.assertEqual(repr(a), 'sllist()') 40 | b = sllist([None, 1, 'abc']) 41 | self.assertEqual(repr(b), 'sllist([None, 1, \'abc\'])') 42 | 43 | def test_node_str(self): 44 | a = sllist([None, None]).first 45 | self.assertEqual(str(a), 'sllistnode(None)') 46 | b = sllist([1, None]).first 47 | self.assertEqual(str(b), 'sllistnode(1)') 48 | c = sllist(['abc', None]).first 49 | self.assertEqual(str(c), 'sllistnode(abc)') 50 | 51 | def test_node_repr(self): 52 | a = sllist([None]).first 53 | self.assertEqual(repr(a), '') 54 | b = sllist([1, None]).first 55 | self.assertEqual(repr(b), '') 56 | c = sllist(['abc', None]).first 57 | self.assertEqual(repr(c), '') 58 | 59 | def test_str_recursive_list(self): 60 | ll = sllist() 61 | ll.append(sllistnode(ll)) 62 | self.assertEqual(str(ll), 'sllist([sllist(<...>)])') 63 | 64 | def test_str_recursive_node(self): 65 | ll = self.make_recursive_node_list() 66 | self.assertEqual(str(ll), 'sllist([sllistnode(sllistnode(<...>))])') 67 | 68 | def test_repr_recursive_list(self): 69 | ll = sllist() 70 | ll.append(sllistnode(ll)) 71 | self.assertEqual(repr(ll), 'sllist([sllist(<...>)])') 72 | 73 | def test_repr_recursive_node_list(self): 74 | ll = self.make_recursive_node_list() 75 | self.assertEqual(repr(ll), 'sllist([))>])') 76 | 77 | def make_recursive_node_list(self): 78 | ll = sllist() 79 | node = sllistnode() 80 | node.value = node 81 | ll.append(node) 82 | return ll 83 | 84 | def test_cmp(self): 85 | a = sllist(py23_xrange(0, 1100)) 86 | b = sllist(py23_xrange(0, 1101)) 87 | c = sllist([1, 2, 3, 4]) 88 | d = sllist([1, 2, 3, 5]) 89 | e = sllist([1, 0, 0, 0]) 90 | f = sllist([0, 0, 0, 0]) 91 | self.assertEqual(py23_cmp(a, a), 0) 92 | self.assertEqual(py23_cmp(a, b), -1) 93 | self.assertEqual(py23_cmp(b, a), 1) 94 | self.assertEqual(py23_cmp(c, d), -1) 95 | self.assertEqual(py23_cmp(d, c), 1) 96 | self.assertEqual(py23_cmp(e, f), 1) 97 | self.assertEqual(py23_cmp(f, e), -1) 98 | 99 | def test_cmp_nonlist(self): 100 | a = sllist(py23_xrange(0, 1100)) 101 | b = [py23_xrange(0, 1100)] 102 | if sys.hexversion < 0x03000000: 103 | # actual order is not specified by language 104 | self.assertNotEqual(py23_cmp(a, b), 0) 105 | self.assertNotEqual(py23_cmp(b, a), 0) 106 | self.assertNotEqual(py23_cmp([], a), 0) 107 | self.assertNotEqual(py23_cmp(a, []), 0) 108 | 109 | def test_eq(self): 110 | a = sllist(py23_xrange(0, 1100)) 111 | b = sllist(py23_xrange(0, 1101)) 112 | c = sllist([1, 2, 3, 4]) 113 | d = sllist([1, 2, 3, 5]) 114 | e = sllist([1, 0, 0, 0]) 115 | f = sllist([0, 0, 0, 0]) 116 | self.assertTrue(sllist() == sllist()) 117 | self.assertTrue(a == a) 118 | self.assertFalse(sllist() == a) 119 | self.assertFalse(a == sllist()) 120 | self.assertFalse(a == b) 121 | self.assertFalse(b == a) 122 | self.assertFalse(c == d) 123 | self.assertFalse(d == c) 124 | self.assertFalse(e == f) 125 | self.assertFalse(f == e) 126 | 127 | def test_ne(self): 128 | a = sllist(py23_xrange(0, 1100)) 129 | b = sllist(py23_xrange(0, 1101)) 130 | c = sllist([1, 2, 3, 4]) 131 | d = sllist([1, 2, 3, 5]) 132 | e = sllist([1, 0, 0, 0]) 133 | f = sllist([0, 0, 0, 0]) 134 | self.assertFalse(sllist() != sllist()) 135 | self.assertFalse(a != a) 136 | self.assertTrue(sllist() != a) 137 | self.assertTrue(a != sllist()) 138 | self.assertTrue(a != b) 139 | self.assertTrue(b != a) 140 | self.assertTrue(c != d) 141 | self.assertTrue(d != c) 142 | self.assertTrue(e != f) 143 | self.assertTrue(f != e) 144 | 145 | def test_lt(self): 146 | a = sllist(py23_xrange(0, 1100)) 147 | b = sllist(py23_xrange(0, 1101)) 148 | c = sllist([1, 2, 3, 4]) 149 | d = sllist([1, 2, 3, 5]) 150 | e = sllist([1, 0, 0, 0]) 151 | f = sllist([0, 0, 0, 0]) 152 | self.assertFalse(sllist() < sllist()) 153 | self.assertFalse(a < a) 154 | self.assertTrue(sllist() < a) 155 | self.assertFalse(a < sllist()) 156 | self.assertTrue(a < b) 157 | self.assertFalse(b < a) 158 | self.assertTrue(c < d) 159 | self.assertFalse(d < c) 160 | self.assertFalse(e < f) 161 | self.assertTrue(f < e) 162 | 163 | def test_gt(self): 164 | a = sllist(py23_xrange(0, 1100)) 165 | b = sllist(py23_xrange(0, 1101)) 166 | c = sllist([1, 2, 3, 4]) 167 | d = sllist([1, 2, 3, 5]) 168 | e = sllist([1, 0, 0, 0]) 169 | f = sllist([0, 0, 0, 0]) 170 | self.assertFalse(sllist() > sllist()) 171 | self.assertFalse(a > a) 172 | self.assertFalse(sllist() > a) 173 | self.assertTrue(a > sllist()) 174 | self.assertFalse(a > b) 175 | self.assertTrue(b > a) 176 | self.assertFalse(c > d) 177 | self.assertTrue(d > c) 178 | self.assertTrue(e > f) 179 | self.assertFalse(f > e) 180 | 181 | def test_le(self): 182 | a = sllist(py23_xrange(0, 1100)) 183 | b = sllist(py23_xrange(0, 1101)) 184 | c = sllist([1, 2, 3, 4]) 185 | d = sllist([1, 2, 3, 5]) 186 | e = sllist([1, 0, 0, 0]) 187 | f = sllist([0, 0, 0, 0]) 188 | self.assertTrue(sllist() <= sllist()) 189 | self.assertTrue(a <= a) 190 | self.assertTrue(sllist() <= a) 191 | self.assertFalse(a <= sllist()) 192 | self.assertTrue(a <= b) 193 | self.assertFalse(b <= a) 194 | self.assertTrue(c <= d) 195 | self.assertFalse(d <= c) 196 | self.assertFalse(e <= f) 197 | self.assertTrue(f <= e) 198 | 199 | def test_ge(self): 200 | a = sllist(py23_xrange(0, 1100)) 201 | b = sllist(py23_xrange(0, 1101)) 202 | c = sllist([1, 2, 3, 4]) 203 | d = sllist([1, 2, 3, 5]) 204 | e = sllist([1, 0, 0, 0]) 205 | f = sllist([0, 0, 0, 0]) 206 | self.assertTrue(sllist() >= sllist()) 207 | self.assertTrue(a >= a) 208 | self.assertFalse(sllist() >= a) 209 | self.assertTrue(a >= sllist()) 210 | self.assertFalse(a >= b) 211 | self.assertTrue(b >= a) 212 | self.assertFalse(c >= d) 213 | self.assertTrue(d >= c) 214 | self.assertTrue(e >= f) 215 | self.assertFalse(f >= e) 216 | 217 | def test_nodeat(self): 218 | ref = py23_range(0, 1024, 4) 219 | ll = sllist(ref) 220 | for idx in py23_xrange(len(ll)): 221 | self.assertTrue(isinstance(ll.nodeat(idx), sllistnode)) 222 | self.assertEqual(ll.nodeat(idx).value, ref[idx]) 223 | for idx in py23_xrange(len(ll)): 224 | self.assertTrue(isinstance(ll.nodeat(idx), sllistnode)) 225 | self.assertEqual(ll.nodeat(-idx - 1).value, ref[-idx - 1]) 226 | self.assertRaises(TypeError, ll.nodeat, None) 227 | self.assertRaises(TypeError, ll.nodeat, 'abc') 228 | self.assertRaises(IndexError, ll.nodeat, len(ref)) 229 | self.assertRaises(IndexError, ll.nodeat, -len(ref) - 1) 230 | 231 | def test_nodeat_empty(self): 232 | ll = sllist() 233 | self.assertRaises(TypeError, ll.nodeat, None) 234 | self.assertRaises(TypeError, ll.nodeat, 'abc') 235 | self.assertRaises(IndexError, ll.nodeat, 0) 236 | self.assertRaises(IndexError, ll.nodeat, -1) 237 | 238 | def test_iter(self): 239 | ref = py23_range(0, 1024, 4) 240 | ll = sllist(ref) 241 | idx = 0 242 | for val in ll: 243 | self.assertNotIsInstance(val, sllistnode) 244 | self.assertEqual(val, ref[idx]) 245 | idx += 1 246 | self.assertEqual(idx, len(ref)) 247 | 248 | def test_iter_on_iterator_returns_same_object(self): 249 | ll = sllist([0, 1, 2, 3]) 250 | first_iter = iter(ll) 251 | second_iter = iter(first_iter) 252 | self.assertIsNotNone(second_iter) 253 | self.assertIs(first_iter, second_iter) 254 | 255 | def test_iter_with_empty(self): 256 | ll = sllist() 257 | count = 0 258 | for val in ll: 259 | count += 1 260 | self.assertEqual(count, 0) 261 | 262 | def test_iter_with_appended_node(self): 263 | ll = sllist() 264 | ll.append('initial item') 265 | 266 | appended_item_visited = False 267 | 268 | for x in ll: 269 | if x == 'initial item': 270 | ll.append('new item') 271 | elif x == 'new item': 272 | appended_item_visited = True 273 | 274 | self.assertTrue(appended_item_visited) 275 | 276 | def test_iter_with_removed_node(self): 277 | ll = sllist(['x', 'removed item']) 278 | 279 | for x in ll: 280 | self.assertNotEqual(x, 'removed item') 281 | ll.remove(ll.last) 282 | 283 | def test_itervalues(self): 284 | ref = py23_range(0, 1024, 4) 285 | ll = sllist(ref) 286 | idx = 0 287 | for val in ll.itervalues(): 288 | self.assertNotIsInstance(val, sllistnode) 289 | self.assertEqual(val, ref[idx]) 290 | idx += 1 291 | self.assertEqual(idx, len(ref)) 292 | 293 | def test_iter_on_itervalues_iterator_returns_same_object(self): 294 | ll = sllist([0, 1, 2, 3]) 295 | first_iter = ll.itervalues() 296 | second_iter = iter(first_iter) 297 | self.assertIsNotNone(second_iter) 298 | self.assertIs(first_iter, second_iter) 299 | 300 | def test_itervalues_with_empty_list(self): 301 | ll = sllist() 302 | count = 0 303 | for val in ll.itervalues(): 304 | count += 1 305 | self.assertEqual(count, 0) 306 | 307 | def test_itervalues_with_appended_node(self): 308 | ll = sllist(['initial item']) 309 | 310 | appended_item_visited = False 311 | 312 | for x in ll.itervalues(): 313 | if x == 'initial item': 314 | ll.append('new item') 315 | elif x == 'new item': 316 | appended_item_visited = True 317 | 318 | self.assertTrue(appended_item_visited) 319 | 320 | def test_itervalues_with_removed_node(self): 321 | ll = sllist(['x', 'removed item']) 322 | 323 | for x in ll.itervalues(): 324 | self.assertNotEqual(x, 'removed item') 325 | ll.remove(ll.last) 326 | 327 | def test_iternodes(self): 328 | ref = list(py23_range(0, 1024, 4)) 329 | ll = sllist(ref) 330 | idx = 0 331 | for node in ll.iternodes(): 332 | self.assertIsInstance(node, sllistnode) 333 | self.assertIs(node, ll.nodeat(idx)) 334 | idx += 1 335 | self.assertEqual(idx, len(ref)) 336 | 337 | def test_iter_on_iternodes_iterator_returns_same_object(self): 338 | ll = sllist([0, 1, 2, 3]) 339 | first_iter = ll.iternodes() 340 | second_iter = iter(first_iter) 341 | self.assertIsNotNone(second_iter) 342 | self.assertIs(first_iter, second_iter) 343 | 344 | def test_iternodes_with_empty_list(self): 345 | ll = sllist() 346 | count = 0 347 | for node in ll.iternodes(): 348 | count += 1 349 | self.assertEqual(count, 0) 350 | 351 | def test_iternodes_with_appended_node(self): 352 | ll = sllist(['initial item']) 353 | 354 | appended_node = None 355 | appended_node_visited = False 356 | 357 | for node in ll.iternodes(): 358 | if node == ll.first: 359 | appended_node = ll.append('new item') 360 | elif node is not None and node is appended_node: 361 | appended_node_visited = True 362 | 363 | self.assertTrue(appended_node_visited) 364 | 365 | def test_iternodes_with_removed_node(self): 366 | ll = sllist(['x', 'removed item']) 367 | removed_node = ll.last 368 | 369 | for node in ll.iternodes(): 370 | self.assertNotEqual(node, removed_node) 371 | ll.remove(removed_node) 372 | 373 | def test_reversed(self): 374 | ref = py23_range(0, 1024, 4) 375 | ll = sllist(ref) 376 | idx = len(ref) - 1 377 | for val in reversed(ll): 378 | self.assertFalse(isinstance(val, sllistnode)) 379 | self.assertEqual(val, ref[idx]) 380 | idx -= 1 381 | self.assertEqual(idx, -1) 382 | 383 | def test_reversed_empty(self): 384 | ll = sllist() 385 | count = 0 386 | for val in reversed(ll): 387 | count += 1 388 | self.assertEqual(count, 0) 389 | 390 | def test_append_left(self): 391 | ll = sllist([1, 2, 3, 4]) 392 | ll.appendleft(5) 393 | self.assertEqual([5, 1, 2, 3, 4], list(ll)) 394 | 395 | def test_append_right(self): 396 | ll = sllist([1, 2, 3, 4]) 397 | ll.appendright(5) 398 | self.assertEqual([1, 2, 3, 4, 5], list(ll)) 399 | 400 | def test_appendnode(self): 401 | ll = sllist([1, 2, 3, 4]) 402 | node = sllistnode(5) 403 | ll.appendnode(node) 404 | self.assertEqual([1, 2, 3, 4, 5], list(ll)) 405 | self.assertIs(node, ll.last) 406 | 407 | def test_appendnode_with_bad_argument_type(self): 408 | ll = sllist() 409 | self.assertRaises(TypeError, ll.appendnode, 'non-node argument') 410 | 411 | def test_appendnode_with_already_owned_node(self): 412 | ll = sllist() 413 | other_list = sllist([1234]) 414 | self.assertRaises(ValueError, ll.appendnode, other_list.first) 415 | 416 | def test_appendnode_refcount_update(self): 417 | ll = sllist() 418 | node = ll.appendnode(sllistnode(1234)) 419 | self.assertGreaterEqual(sys.getrefcount(node), 3) 420 | 421 | def test_pop_left_from_one_elem(self): 422 | ll = sllist(py23_xrange(0, 100)) 423 | dd = ll.popleft() 424 | self.assertEqual(dd, 0) 425 | 426 | def test_pop_right_from_one_elem(self): 427 | ll = sllist(py23_xrange(0, 100)) 428 | dd = ll.popright() 429 | self.assertEqual(dd, 99) 430 | 431 | def test_pop_right_from_n_elem(self): 432 | ll = sllist(py23_xrange(0, 100)) 433 | dd = ll.popright() 434 | self.assertEqual(dd, 99) 435 | 436 | def test_get_node_at_from_n_elem(self): 437 | ll = sllist(py23_xrange(0, 100)) 438 | self.assertEqual(50, ll[50]) 439 | 440 | def test_remove_from_n_elem(self): 441 | ll = sllist() 442 | nn = sllistnode() 443 | ll.append(nn) 444 | to_del = ll.nodeat(0) 445 | ll.remove(to_del) 446 | self.assertEqual(None, None) 447 | 448 | def test_insert_after(self): 449 | ll = sllist([1, 3, '123']) 450 | ll.insertafter(100, ll.first) 451 | self.assertEqual([1, 100, 3, '123'], list(ll)) 452 | 453 | def test_insert_before(self): 454 | ll = sllist([1, 3, '123']) 455 | ll.insertbefore(100, ll.first) 456 | self.assertEqual([100, 1, 3, '123'], list(ll)) 457 | 458 | def test_insert_value_after(self): 459 | ll = sllist(py23_xrange(4)) 460 | ref = sllist([0, 1, 2, 10, 3]) 461 | prev = ll.nodeat(2) 462 | next = ll.nodeat(3) 463 | arg_node = sllistnode(10) 464 | new_node = ll.insertafter(arg_node, ll.nodeat(2)) 465 | self.assertNotEqual(new_node, arg_node) 466 | self.assertEqual(new_node.value, 10) 467 | self.assertEqual(new_node.next, next) 468 | self.assertEqual(prev.next, new_node) 469 | self.assertEqual(ll, ref) 470 | 471 | def test_insert_value_after_last(self): 472 | ll = sllist(py23_xrange(4)) 473 | ref = sllist([0, 1, 2, 3, 10]) 474 | prev = ll.nodeat(3) 475 | arg_node = sllistnode(10) 476 | new_node = ll.insertafter(arg_node, ll.nodeat(-1)) 477 | self.assertNotEqual(new_node, arg_node) 478 | self.assertEqual(new_node.value, 10) 479 | self.assertEqual(new_node.next, None) 480 | self.assertEqual(prev.next, new_node) 481 | self.assertEqual(new_node, ll.last) 482 | self.assertEqual(ll, ref) 483 | 484 | def test_insert_value_before(self): 485 | ll = sllist(py23_xrange(4)) 486 | ref = sllist([0, 1, 10, 2, 3]) 487 | prev = ll.nodeat(1) 488 | next = ll.nodeat(2) 489 | arg_node = sllistnode(10) 490 | new_node = ll.insertbefore(arg_node, ll.nodeat(2)) 491 | self.assertNotEqual(new_node, arg_node) 492 | self.assertEqual(new_node.value, 10) 493 | self.assertEqual(new_node.next, next) 494 | self.assertEqual(prev.next, new_node) 495 | self.assertEqual(ll, ref) 496 | 497 | def test_insert_value_before_first(self): 498 | ll = sllist(py23_xrange(4)) 499 | ref = sllist([10, 0, 1, 2, 3]) 500 | next = ll.nodeat(0) 501 | arg_node = sllistnode(10) 502 | new_node = ll.insertbefore(arg_node, ll.nodeat(0)) 503 | self.assertNotEqual(new_node, arg_node) 504 | self.assertEqual(new_node.value, 10) 505 | self.assertEqual(new_node.next, next) 506 | self.assertEqual(new_node, ll.first) 507 | self.assertEqual(ll, ref) 508 | 509 | def test_insert_invalid_ref(self): 510 | ll = sllist([1, 2, 3, 4]) 511 | self.assertRaises(TypeError, ll.insertafter, 10, 1) 512 | self.assertRaises(TypeError, ll.insertafter, 10, 'abc') 513 | self.assertRaises(TypeError, ll.insertafter, 10, []) 514 | self.assertRaises(ValueError, ll.insertafter, 10, sllistnode()) 515 | self.assertRaises(TypeError, ll.insertbefore, 10, 1) 516 | self.assertRaises(TypeError, ll.insertbefore, 10, 'abc') 517 | self.assertRaises(TypeError, ll.insertbefore, 10, []) 518 | self.assertRaises(ValueError, ll.insertbefore, 10, sllistnode()) 519 | 520 | def test_insert_node_after_first(self): 521 | ll = sllist([1, 3, '123']) 522 | arg_node = sllistnode(100) 523 | new_node = ll.insertnodeafter(arg_node, ll.first) 524 | self.assertEqual([1, 100, 3, '123'], list(ll)) 525 | self.assertEqual(new_node, arg_node) 526 | 527 | def test_insert_node_before_first(self): 528 | ll = sllist([1, 3, '123']) 529 | arg_node = sllistnode(100) 530 | new_node = ll.insertnodebefore(arg_node, ll.first) 531 | self.assertEqual([100, 1, 3, '123'], list(ll)) 532 | self.assertEqual(new_node, arg_node) 533 | 534 | def test_insert_node_after_another(self): 535 | ll = sllist(py23_xrange(4)) 536 | ref = sllist([0, 1, 2, 10, 3]) 537 | prev = ll.nodeat(2) 538 | next = ll.nodeat(3) 539 | arg_node = sllistnode(10) 540 | new_node = ll.insertnodeafter(arg_node, ll.nodeat(2)) 541 | self.assertEqual(new_node, arg_node) 542 | self.assertEqual(new_node.value, 10) 543 | self.assertEqual(new_node.next, next) 544 | self.assertEqual(prev.next, new_node) 545 | self.assertEqual(ll, ref) 546 | 547 | def test_insert_node_after_last(self): 548 | ll = sllist(py23_xrange(4)) 549 | ref = sllist([0, 1, 2, 3, 10]) 550 | prev = ll.nodeat(3) 551 | arg_node = sllistnode(10) 552 | new_node = ll.insertnodeafter(arg_node, ll.nodeat(-1)) 553 | self.assertEqual(new_node, arg_node) 554 | self.assertEqual(new_node.value, 10) 555 | self.assertEqual(new_node.next, None) 556 | self.assertEqual(prev.next, new_node) 557 | self.assertEqual(new_node, ll.last) 558 | self.assertEqual(ll, ref) 559 | 560 | def test_insert_node_before_another(self): 561 | ll = sllist(py23_xrange(4)) 562 | ref = sllist([0, 1, 10, 2, 3]) 563 | prev = ll.nodeat(1) 564 | next = ll.nodeat(2) 565 | arg_node = sllistnode(10) 566 | new_node = ll.insertnodebefore(arg_node, ll.nodeat(2)) 567 | self.assertEqual(new_node, arg_node) 568 | self.assertEqual(new_node.value, 10) 569 | self.assertEqual(new_node.next, next) 570 | self.assertEqual(prev.next, new_node) 571 | self.assertEqual(ll, ref) 572 | 573 | def test_insert_node_before_node_at_index_0(self): 574 | ll = sllist(py23_xrange(4)) 575 | ref = sllist([10, 0, 1, 2, 3]) 576 | next = ll.nodeat(0) 577 | arg_node = sllistnode(10) 578 | new_node = ll.insertnodebefore(arg_node, ll.nodeat(0)) 579 | self.assertEqual(new_node, arg_node) 580 | self.assertEqual(new_node.value, 10) 581 | self.assertEqual(new_node.next, next) 582 | self.assertEqual(new_node, ll.first) 583 | self.assertEqual(ll, ref) 584 | 585 | def test_insert_node_after_with_bad_argument_type(self): 586 | ll = sllist([1234]) 587 | self.assertRaises( 588 | TypeError, ll.insertnodeafter, 'non-node argument', ll.first) 589 | 590 | def test_insert_node_before_with_bad_argument_type(self): 591 | ll = sllist([1234]) 592 | self.assertRaises( 593 | TypeError, ll.insertnodebefore, 'non-node argument', ll.first) 594 | 595 | def test_insert_node_after_with_already_owned_node(self): 596 | ll = sllist([1234]) 597 | other_list = sllist([5678]) 598 | self.assertRaises( 599 | ValueError, ll.insertnodeafter, other_list.first, ll.first) 600 | 601 | def test_insert_node_before_with_already_owned_node(self): 602 | ll = sllist([1234]) 603 | other_list = sllist([5678]) 604 | self.assertRaises( 605 | ValueError, ll.insertnodebefore, other_list.first, ll.first) 606 | 607 | def test_insert_node_with_invalid_ref(self): 608 | ll = sllist([1, 2, 3, 4]) 609 | self.assertRaises(TypeError, ll.insertnodeafter, sllistnode(10), 1) 610 | self.assertRaises(TypeError, ll.insertnodeafter, sllistnode(10), 'abc') 611 | self.assertRaises(TypeError, ll.insertnodeafter, sllistnode(10), []) 612 | self.assertRaises( 613 | ValueError, ll.insertnodeafter, sllistnode(10), sllistnode()) 614 | self.assertRaises(TypeError, ll.insertnodebefore, sllistnode(10), 1) 615 | self.assertRaises(TypeError, ll.insertnodebefore, sllistnode(10), 'abc') 616 | self.assertRaises(TypeError, ll.insertnodebefore, sllistnode(10), []) 617 | self.assertRaises( 618 | ValueError, ll.insertnodebefore, sllistnode(10), sllistnode()) 619 | 620 | def test_insert_node_after_refcount_update(self): 621 | ll = sllist([1234]) 622 | node = ll.insertnodeafter(sllistnode(5678), ll.nodeat(0)) 623 | self.assertGreaterEqual(sys.getrefcount(node), 3) 624 | 625 | def test_insert_node_before_refcount_update(self): 626 | ll = sllist([1234]) 627 | node = ll.insertnodebefore(sllistnode(5678), ll.nodeat(0)) 628 | self.assertGreaterEqual(sys.getrefcount(node), 3) 629 | 630 | def test_append(self): 631 | ll = sllist(py23_xrange(4)) 632 | ref = sllist([0, 1, 2, 3, 10]) 633 | prev = ll.nodeat(-1) 634 | arg_node = sllistnode(10) 635 | new_node = ll.append(arg_node) 636 | self.assertNotEqual(new_node, arg_node) 637 | self.assertEqual(new_node.value, 10) 638 | self.assertEqual(new_node.next, None) 639 | self.assertEqual(prev.next, new_node) 640 | self.assertEqual(ll.last, new_node) 641 | self.assertEqual(ll, ref) 642 | 643 | def test_appendleft(self): 644 | ll = sllist(py23_xrange(4)) 645 | ref = sllist([10, 0, 1, 2, 3]) 646 | next = ll.nodeat(0) 647 | arg_node = sllistnode(10) 648 | new_node = ll.appendleft(arg_node) 649 | self.assertNotEqual(new_node, arg_node) 650 | self.assertEqual(new_node.value, 10) 651 | self.assertEqual(new_node.next, next) 652 | self.assertEqual(ll.first, new_node) 653 | self.assertEqual(ll, ref) 654 | 655 | def test_appendright(self): 656 | ll = sllist(py23_xrange(4)) 657 | ref = sllist([0, 1, 2, 3, 10]) 658 | prev = ll.nodeat(-1) 659 | arg_node = sllistnode(10) 660 | new_node = ll.appendright(arg_node) 661 | self.assertNotEqual(new_node, arg_node) 662 | self.assertEqual(new_node.value, 10) 663 | self.assertEqual(new_node.next, None) 664 | self.assertEqual(prev.next, new_node) 665 | self.assertEqual(ll.last, new_node) 666 | self.assertEqual(ll, ref) 667 | 668 | def test_extend(self): 669 | a_ref = py23_range(0, 1024, 4) 670 | b_ref = py23_range(8092, 8092 + 1024, 4) 671 | b = sllist(b_ref) 672 | ab_ref = sllist(a_ref + b_ref) 673 | a = sllist(a_ref) 674 | a.extend(b) 675 | self.assertEqual(a, ab_ref) 676 | self.assertEqual(len(a), len(ab_ref)) 677 | a = sllist(a_ref) 678 | a.extend(b_ref) 679 | self.assertEqual(a, ab_ref) 680 | self.assertEqual(len(a), len(ab_ref)) 681 | a = sllist(a_ref) 682 | a.extend(a) 683 | self.assertEqual(a, sllist(a_ref + a_ref)) 684 | self.assertEqual(len(a), len(a_ref) * 2) 685 | 686 | def test_extend_empty(self): 687 | filled_ref = py23_range(0, 1024, 4) 688 | filled = sllist(filled_ref) 689 | empty = sllist() 690 | empty.extend(empty) 691 | self.assertEqual(empty, sllist([] + [])) 692 | self.assertEqual(len(empty), 0) 693 | empty = sllist() 694 | empty.extend(filled) 695 | self.assertEqual(empty, sllist([] + filled_ref)) 696 | self.assertEqual(len(empty), len(filled_ref)) 697 | empty = sllist() 698 | filled.extend(empty) 699 | self.assertEqual(filled, sllist(filled_ref + [])) 700 | self.assertEqual(len(filled), len(filled_ref)) 701 | 702 | def test_extendleft(self): 703 | a_ref = py23_range(0, 1024, 4) 704 | b_ref = py23_range(8092, 8092 + 1024, 4) 705 | b = sllist(b_ref) 706 | ab_ref = sllist(list(reversed(b_ref)) + a_ref) 707 | a = sllist(a_ref) 708 | a.extendleft(b) 709 | self.assertEqual(a, ab_ref) 710 | self.assertEqual(len(a), len(ab_ref)) 711 | a = sllist(a_ref) 712 | a.extendleft(b_ref) 713 | self.assertEqual(a, ab_ref) 714 | self.assertEqual(len(a), len(ab_ref)) 715 | a = sllist(a_ref) 716 | a.extendleft(a) 717 | self.assertEqual(a, sllist(list(reversed(a_ref)) + a_ref)) 718 | self.assertEqual(len(a), len(a_ref) * 2) 719 | 720 | def test_extendleft_empty(self): 721 | filled_ref = py23_range(0, 1024, 4) 722 | filled = sllist(filled_ref) 723 | empty = sllist() 724 | empty.extendleft(empty) 725 | self.assertEqual(empty, sllist([] + [])) 726 | self.assertEqual(len(empty), 0) 727 | empty = sllist() 728 | empty.extendleft(filled) 729 | self.assertEqual(empty, sllist(list(reversed(filled_ref)) + [])) 730 | self.assertEqual(len(empty), len(filled_ref)) 731 | empty = sllist() 732 | filled.extendleft(empty) 733 | self.assertEqual(filled, sllist(list(reversed([])) + filled_ref)) 734 | self.assertEqual(len(filled), len(filled_ref)) 735 | 736 | def test_extendright(self): 737 | a_ref = py23_range(0, 1024, 4) 738 | b_ref = py23_range(8092, 8092 + 1024, 4) 739 | b = sllist(b_ref) 740 | ab_ref = sllist(a_ref + b_ref) 741 | a = sllist(a_ref) 742 | a.extendright(b) 743 | self.assertEqual(a, ab_ref) 744 | self.assertEqual(len(a), len(ab_ref)) 745 | a = sllist(a_ref) 746 | a.extendright(b_ref) 747 | self.assertEqual(a, ab_ref) 748 | self.assertEqual(len(a), len(ab_ref)) 749 | a = sllist(a_ref) 750 | a.extendright(a) 751 | self.assertEqual(a, sllist(a_ref + a_ref)) 752 | self.assertEqual(len(a), len(a_ref) * 2) 753 | 754 | def test_extendright_empty(self): 755 | filled_ref = py23_range(0, 1024, 4) 756 | filled = sllist(filled_ref) 757 | empty = sllist() 758 | empty.extendright(empty) 759 | self.assertEqual(empty, sllist([] + [])) 760 | self.assertEqual(len(empty), 0) 761 | empty = sllist() 762 | empty.extendright(filled) 763 | self.assertEqual(empty, sllist([] + filled_ref)) 764 | self.assertEqual(len(empty), len(filled_ref)) 765 | empty = sllist() 766 | filled.extendright(empty) 767 | self.assertEqual(filled, sllist(filled_ref + [])) 768 | self.assertEqual(len(filled), len(filled_ref)) 769 | 770 | def test_clear_empty(self): 771 | empty_list = sllist() 772 | empty_list.clear() 773 | self.assertEqual(empty_list.first, None) 774 | self.assertEqual(empty_list.last, None) 775 | self.assertEqual(empty_list.size, 0) 776 | self.assertEqual(list(empty_list), []) 777 | 778 | def test_clear(self): 779 | ll = sllist(py23_xrange(0, 1024, 4)) 780 | del_node = ll.nodeat(4) 781 | ll.clear() 782 | self.assertEqual(ll.first, None) 783 | self.assertEqual(ll.last, None) 784 | self.assertEqual(ll.size, 0) 785 | self.assertEqual(list(ll), []) 786 | self.assertEqual(del_node.next, None) 787 | 788 | def test_pop(self): 789 | ref = py23_range(0, 1024, 4) 790 | ll = sllist(ref) 791 | result = ll.pop() 792 | self.assertEqual(result, ref[-1]) 793 | self.assertEqual(len(ll), len(ref) - 1) 794 | self.assertEqual(ll.size, len(ref) - 1) 795 | self.assertEqual(ll.last.value, ref[-2]) 796 | self.assertEqual(list(ll), ref[:-1]) 797 | 798 | def test_node_after_pop(self): 799 | ll = sllist([1, 2]) 800 | del_node = ll.last 801 | ll.pop() 802 | self.assertIs(del_node.next, None) 803 | self.assertIs(del_node.owner, None) 804 | 805 | def test_popleft(self): 806 | ref = py23_range(0, 1024, 4) 807 | ll = sllist(ref) 808 | result = ll.popleft() 809 | self.assertEqual(result, ref[0]) 810 | self.assertEqual(len(ll), len(ref) - 1) 811 | self.assertEqual(ll.size, len(ref) - 1) 812 | self.assertEqual(ll.first.value, ref[1]) 813 | self.assertEqual(list(ll), ref[1:]) 814 | 815 | def test_node_after_popleft(self): 816 | ll = sllist([1, 2]) 817 | del_node = ll.first 818 | ll.popleft() 819 | self.assertIs(del_node.next, None) 820 | self.assertIs(del_node.owner, None) 821 | 822 | def test_popright(self): 823 | ref = py23_range(0, 1024, 4) 824 | ll = sllist(ref) 825 | result = ll.popright() 826 | self.assertEqual(result, ref[-1]) 827 | self.assertEqual(len(ll), len(ref) - 1) 828 | self.assertEqual(ll.size, len(ref) - 1) 829 | self.assertEqual(ll.last.value, ref[-2]) 830 | self.assertEqual(list(ll), ref[:-1]) 831 | 832 | def test_node_after_popright(self): 833 | ll = sllist([1, 2]) 834 | del_node = ll.last 835 | ll.popright() 836 | self.assertIs(del_node.next, None) 837 | self.assertIs(del_node.owner, None) 838 | 839 | def test_pop_from_empty_list(self): 840 | ll = sllist() 841 | self.assertRaises(ValueError, ll.pop) 842 | self.assertRaises(ValueError, ll.popleft) 843 | self.assertRaises(ValueError, ll.popright) 844 | 845 | def test_remove(self): 846 | ref = py23_range(0, 1024, 4) 847 | ll = sllist(ref) 848 | prev_node = ll.nodeat(3) 849 | del_node = ll.nodeat(4) 850 | next_node = ll.nodeat(5) 851 | result = ll.remove(del_node) 852 | ref_result = ref[4] 853 | del ref[4] 854 | self.assertEqual(list(ll), ref) 855 | self.assertEqual(result, ref_result) 856 | self.assertEqual(len(ll), len(ref)) 857 | self.assertEqual(ll.size, len(ref)) 858 | self.assertEqual(prev_node.next, next_node) 859 | 860 | def test_remove_after_remove(self): 861 | ll = sllist([1, 2, 3]) 862 | del_node = ll.nodeat(1) 863 | ll.remove(del_node) 864 | self.assertIs(del_node.next, None) 865 | self.assertIs(del_node.owner, None) 866 | 867 | def test_remove_from_empty_list(self): 868 | ll = sllist() 869 | self.assertRaises(ValueError, ll.remove, sllistnode()) 870 | 871 | def test_remove_invalid_node(self): 872 | ll = sllist([1, 2, 3, 4]) 873 | self.assertRaises(ValueError, ll.remove, sllistnode()) 874 | self.assertEqual(len(ll), 4) 875 | 876 | def test_remove_already_deleted_node(self): 877 | ll = sllist([1, 2, 3, 4]) 878 | node = ll.nodeat(2) 879 | ll.remove(node) 880 | self.assertRaises(ValueError, ll.remove, node) 881 | 882 | def test_rotate_left(self): 883 | for n in py23_xrange(128): 884 | ref = py23_range(32) 885 | split = n % len(ref) 886 | ref_result = ref[split:] + ref[:split] 887 | ll = sllist(ref) 888 | new_first = ll.nodeat(split) 889 | new_last = ll.nodeat(split - 1) 890 | ll.rotate(-n) 891 | self.assertEqual(list(ll), ref_result) 892 | self.assertEqual(ll.first, new_first) 893 | self.assertEqual(ll.last, new_last) 894 | self.assertEqual(ll.size, len(ref)) 895 | self.assertEqual(ll.last.next, None) 896 | 897 | def test_rotate_right(self): 898 | for n in py23_xrange(128): 899 | ref = py23_range(32) 900 | split = n % len(ref) 901 | ref_result = ref[-split:] + ref[:-split] 902 | ll = sllist(ref) 903 | new_first = ll.nodeat(-split) 904 | last_idx = -split - 1 905 | new_last = ll.nodeat(last_idx) 906 | ll.rotate(n) 907 | self.assertEqual(list(ll), ref_result) 908 | self.assertEqual(ll.first, new_first) 909 | self.assertEqual(ll.last, new_last) 910 | self.assertEqual(ll.size, len(ref)) 911 | self.assertEqual(ll.last.next, None) 912 | 913 | def test_rotate_left_empty(self): 914 | for n in py23_xrange(4): 915 | ll = sllist() 916 | ll.rotate(-n) 917 | self.assertEqual(ll.first, None) 918 | self.assertEqual(ll.last, None) 919 | self.assertEqual(ll.size, 0) 920 | 921 | def test_rotate_right_empty(self): 922 | for n in py23_xrange(4): 923 | ll = sllist() 924 | ll.rotate(n) 925 | self.assertEqual(ll.first, None) 926 | self.assertEqual(ll.last, None) 927 | self.assertEqual(ll.size, 0) 928 | 929 | def test_getitem(self): 930 | ref = py23_range(0, 1024, 4) 931 | ll = sllist(ref) 932 | for idx in py23_xrange(len(ll)): 933 | self.assertFalse(isinstance(ll[idx], sllistnode)) 934 | self.assertEqual(ll[idx], ref[idx]) 935 | for idx in py23_xrange(len(ll)): 936 | self.assertFalse(isinstance(ll[idx], sllistnode)) 937 | self.assertEqual(ll[-idx - 1], ref[-idx - 1]) 938 | self.assertRaises(TypeError, ll.__getitem__, None) 939 | self.assertRaises(TypeError, ll.__getitem__, 'abc') 940 | self.assertRaises(IndexError, ll.__getitem__, len(ref)) 941 | self.assertRaises(IndexError, ll.__getitem__, -len(ref) - 1) 942 | 943 | def test_getitem_empty(self): 944 | ll = sllist() 945 | self.assertRaises(TypeError, ll.__getitem__, None) 946 | self.assertRaises(TypeError, ll.__getitem__, 'abc') 947 | self.assertRaises(IndexError, ll.__getitem__, 0) 948 | self.assertRaises(IndexError, ll.__getitem__, -1) 949 | 950 | def test_del(self): 951 | ref = py23_range(0, 1024, 4) 952 | ll = sllist(ref) 953 | del ll[0] 954 | del ref[0] 955 | self.assertEqual(list(ll), ref) 956 | del ll[len(ll) - 1] 957 | del ref[len(ref) - 1] 958 | self.assertEqual(list(ll), ref) 959 | del ll[(len(ll) - 1) // 2] 960 | del ref[(len(ref) - 1) // 2] 961 | self.assertEqual(list(ll), ref) 962 | 963 | def del_item(idx): 964 | del ll[idx] 965 | self.assertRaises(IndexError, del_item, len(ll)) 966 | 967 | for i in py23_xrange(len(ll)): 968 | del ll[0] 969 | self.assertEqual(len(ll), 0) 970 | 971 | def test_node_after_del(self): 972 | ll = sllist([1, 2, 3]) 973 | del_node = ll.nodeat(1) 974 | del ll[1] 975 | self.assertIs(del_node.next, None) 976 | self.assertIs(del_node.owner, None) 977 | 978 | def test_concat(self): 979 | a_ref = py23_range(0, 1024, 4) 980 | a = sllist(a_ref) 981 | b_ref = py23_range(8092, 8092 + 1024, 4) 982 | b = sllist(b_ref) 983 | ab_ref = sllist(a_ref + b_ref) 984 | c = a + b 985 | self.assertEqual(c, ab_ref) 986 | self.assertEqual(len(c), len(ab_ref)) 987 | c = a + b_ref 988 | self.assertEqual(c, ab_ref) 989 | self.assertEqual(len(c), len(ab_ref)) 990 | 991 | def test_concat_empty(self): 992 | empty = sllist() 993 | filled_ref = py23_range(0, 1024, 4) 994 | filled = sllist(filled_ref) 995 | res = empty + empty 996 | self.assertEqual(res, sllist([] + [])) 997 | self.assertEqual(len(res), 0) 998 | res = empty + filled 999 | self.assertEqual(res, sllist([] + filled_ref)) 1000 | self.assertEqual(len(res), len(filled_ref)) 1001 | res = filled + empty 1002 | self.assertEqual(res, sllist(filled_ref + [])) 1003 | self.assertEqual(len(res), len(filled_ref)) 1004 | 1005 | def test_concat_inplace(self): 1006 | a_ref = py23_range(0, 1024, 4) 1007 | b_ref = py23_range(8092, 8092 + 1024, 4) 1008 | b = sllist(b_ref) 1009 | ab_ref = sllist(a_ref + b_ref) 1010 | a = sllist(a_ref) 1011 | a += b 1012 | self.assertEqual(a, ab_ref) 1013 | self.assertEqual(len(a), len(ab_ref)) 1014 | a = sllist(a_ref) 1015 | a += b_ref 1016 | self.assertEqual(a, ab_ref) 1017 | self.assertEqual(len(a), len(ab_ref)) 1018 | a = sllist(a_ref) 1019 | a += a 1020 | self.assertEqual(a, sllist(a_ref + a_ref)) 1021 | self.assertEqual(len(a), len(ab_ref)) 1022 | 1023 | def test_concat_inplace_empty(self): 1024 | filled_ref = py23_range(0, 1024, 4) 1025 | filled = sllist(filled_ref) 1026 | empty = sllist() 1027 | empty += empty 1028 | self.assertEqual(empty, sllist([] + [])) 1029 | self.assertEqual(len(empty), 0) 1030 | empty = sllist() 1031 | empty += filled 1032 | self.assertEqual(empty, sllist([] + filled_ref)) 1033 | self.assertEqual(len(empty), len(filled_ref)) 1034 | empty = sllist() 1035 | filled += empty 1036 | self.assertEqual(filled, sllist(filled_ref + [])) 1037 | self.assertEqual(len(filled), len(filled_ref)) 1038 | 1039 | def test_repeat(self): 1040 | ref = py23_range(0, 1024, 4) 1041 | ll = sllist(ref) 1042 | self.assertEqual(ll * 4, sllist(ref * 4)) 1043 | 1044 | def test_repeat_empty(self): 1045 | ll = sllist() 1046 | self.assertEqual(ll * 4, sllist([] * 4)) 1047 | 1048 | def test_repeat_inplace(self): 1049 | ref = py23_range(0, 1024, 4) 1050 | ll = sllist(ref) 1051 | ll *= 4 1052 | self.assertEqual(ll, sllist(ref * 4)) 1053 | 1054 | def test_repeat_inplace_empty(self): 1055 | ll = sllist() 1056 | ll *= 4 1057 | self.assertEqual(ll, sllist([] * 4)) 1058 | 1059 | def test_list_readonly_attributes(self): 1060 | if sys.hexversion >= 0x03000000: 1061 | expected_error = AttributeError 1062 | else: 1063 | expected_error = TypeError 1064 | 1065 | ll = sllist(py23_range(4)) 1066 | self.assertRaises(expected_error, setattr, ll, 'first', None) 1067 | self.assertRaises(expected_error, setattr, ll, 'last', None) 1068 | self.assertRaises(expected_error, setattr, ll, 'size', None) 1069 | 1070 | def test_node_readonly_attributes(self): 1071 | if sys.hexversion >= 0x03000000: 1072 | expected_error = AttributeError 1073 | else: 1074 | expected_error = TypeError 1075 | 1076 | ll = sllistnode() 1077 | self.assertRaises(expected_error, setattr, ll, 'next', None) 1078 | 1079 | def test_node_owner(self): 1080 | ll = sllist([1234]) 1081 | owner_ref = ll.first.owner 1082 | self.assertIsInstance(owner_ref, weakref.ref) 1083 | self.assertIs(owner_ref(), ll) 1084 | del ll 1085 | self.assertIsNone(owner_ref()) 1086 | 1087 | def test_list_hash(self): 1088 | self.assertEqual(hash(sllist()), hash(sllist())) 1089 | self.assertEqual(hash(sllist(py23_range(0, 1024, 4))), 1090 | hash(sllist(py23_range(0, 1024, 4)))) 1091 | self.assertEqual(hash(sllist([0, 2])), hash(sllist([0.0, 2.0]))) 1092 | self.assertNotEqual(hash(sllist([1, 2])), hash(sllist([2, 1]))) 1093 | 1094 | def test_list_can_be_subclassed(self): 1095 | class DerivedList(sllist): 1096 | pass 1097 | 1098 | def test_list_node_can_be_subclassed(self): 1099 | class DerivedNode(sllistnode): 1100 | pass 1101 | 1102 | def test_cyclic_list_destruction_does_not_release_extra_None_refs(self): 1103 | def create_and_free_lists(): 1104 | for _ in range(10): 1105 | ll = sllist() 1106 | n = sllistnode(ll) 1107 | ll.append(n) 1108 | del ll 1109 | del n 1110 | 1111 | self.assertStableRefCount(create_and_free_lists, None) 1112 | 1113 | def test_cyclic_node_destruction_does_not_release_extra_None_refs(self): 1114 | def create_and_free_lists(): 1115 | for _ in range(10): 1116 | ll = self.make_recursive_node_list() 1117 | del ll 1118 | 1119 | self.assertStableRefCount(create_and_free_lists, None) 1120 | -------------------------------------------------------------------------------- /tests/speed_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from collections import deque 4 | from llist import sllist, dllist 5 | import time 6 | # import gc 7 | # gc.set_debug(gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_STATS) 8 | 9 | num = 10000 10 | 11 | 12 | class Stopwatch(object): 13 | def __init__(self): 14 | self.elapsed = 0.0 15 | self._start_time = None 16 | 17 | def __enter__(self): 18 | self._start_time = time.time() 19 | 20 | def __exit__(self, *exc_details): 21 | stop_time = time.time() 22 | assert self._start_time is not None 23 | self.elapsed += stop_time - self._start_time 24 | 25 | 26 | def append(stopwatch, c): 27 | for i in range(num): 28 | with stopwatch: 29 | c.append(i) 30 | 31 | 32 | def appendleft(stopwatch, c): 33 | for i in range(num): 34 | with stopwatch: 35 | c.appendleft(i) 36 | 37 | 38 | def pop(stopwatch, c): 39 | for i in range(num): 40 | with stopwatch: 41 | c.pop() 42 | 43 | 44 | def popleft(stopwatch, c): 45 | if isinstance(c, deque): 46 | for i in range(num): 47 | with stopwatch: 48 | c.popleft() 49 | else: 50 | for i in range(num): 51 | with stopwatch: 52 | c.pop() 53 | 54 | 55 | def remove(stopwatch, c): 56 | if isinstance(c, deque): 57 | for i in range(0, num, 2): 58 | with stopwatch: 59 | c.remove(i) 60 | else: 61 | nodes = list(c.iternodes()) 62 | for i in range(0, num, 2): 63 | with stopwatch: 64 | c.remove(nodes[i]) 65 | 66 | 67 | def index_iter(stopwatch, c): 68 | for i in range(len(c)): 69 | with stopwatch: 70 | c[i] 71 | 72 | 73 | for container in [deque, dllist, sllist]: 74 | for operation in [append, appendleft, pop, popleft, remove, index_iter]: 75 | stopwatch = Stopwatch() 76 | c = container(range(num)) 77 | operation(stopwatch, c) 78 | print("Completed %s/%s in \t\t%.8f seconds:\t %.1f ops/sec" % ( 79 | container.__name__, 80 | operation.__name__, 81 | stopwatch.elapsed, 82 | num / stopwatch.elapsed if stopwatch.elapsed != 0 else float('inf'))) 83 | --------------------------------------------------------------------------------