diff --git a/README.rst b/README.rst index c125206..7dbb290 100644 --- a/README.rst +++ b/README.rst @@ -137,12 +137,14 @@ for usage documentation. Learning from bidict -------------------- -I have learned a surprisingly large amount -of fascinating advanced Python programming -from working on bidict, -especially in light of its relatively small codebase. -Check out :doc:`learning-from-bidict` [#fn-learning]_ -if you're interested in learning from bidict too. +One of the most rewarding things about working on bidict +– and also the most surprising, +especially in light of the small codebase (just ~600 sloc) – +is how much fun, advanced Python material +has turned up in the process. + +Check out the :doc:`learning-from-bidict` [#fn-learning]_ docs +if you're interested in taking a tour. Contributing diff --git a/_static/custom.css b/_static/custom.css index 2591e37..c5ad6ad 100644 --- a/_static/custom.css +++ b/_static/custom.css @@ -2,7 +2,7 @@ h1, h2, h3, h4, h5 { font-weight: bold !important; } -div#changelog li { +ul.simple li { margin-bottom: 8px; } diff --git a/docs/conf.py b/docs/conf.py index 73893a6..f1f98ed 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,10 +49,12 @@ extensions = [ 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', 'alabaster', ] intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} +todo_include_todos = True # Add any paths that contain templates here, relative to this directory. #templates_path = ['_templates'] diff --git a/docs/learning-from-bidict.rst b/docs/learning-from-bidict.rst index d208054..0359170 100644 --- a/docs/learning-from-bidict.rst +++ b/docs/learning-from-bidict.rst @@ -1,27 +1,109 @@ Learning from bidict -------------------- -I have learned a surprisingly large amount -of fascinating advanced Python programming -in the process of developing bidict, -especially in light of its relatively small codebase. - -Below are some of the fun Python corners I got to explore further +Below are some of the more fascinating Python corners +I got to explore further thanks to working on bidict. -If you are interested in learning more about any of the following, -reading and contributing to bidict's code -could be a great way to do so. -TODO: Expand all of the following, and -include more specific references to bidict's usages. +If you are interested in learning more about any of the following, +reading through or even contributing to bidict's code +could be a great way to get started. + +.. todo:: + + The following is just an outline. + Expand and provide more references and examples. + + +Python's data model +=================== + +- Making an immutable type hashable, + i.e. insertable into :class:`dict`\s and :class:`set`\s + + - See :meth:`object.__hash__` and :meth:`object.__eq__` docs + + - How this affects hashable ordered collections + like :class:`~bidict.FrozenOrderedBidict` + that have an order-insensitive + :meth:`~bidict.FrozenOrderedBidict.__eq__` + + - All contained items must participate in the hash + +- Resulting corner cases produce possibly surprising results: + + - See :ref:`nan-as-key` + + - See + `pywat#38 `_ + for some surprising results when keys of different types compare equal, + or when a hashable type's ``__eq__()`` is intransitive + (as in :class:`~collections.OrderedDict`): + + - "Intransitive equality was a mistake." –Raymond Hettinger + + - Thus :ref:`eq-order-insensitive` for :class:`~bidict.FrozenOrderedBidict` + +- Using :meth:`object.__new__` to bypass default object initialization, + e.g. for better :meth:`~bidict.bidict.copy` performance + + - See `how bidict does this + `_ + +- Overriding :meth:`object.__getattribute__` for custom attribute lookup + + - See :ref:`sorted-bidict-recipes` for example + +- Using :meth:`object.__reduce__` to make an object pickleable + that otherwise wouldn't be, + due to e.g. using weakrefs (see below) + + +Using :mod:`weakref` +==================== + +- See :ref:`inv-avoids-reference-cycles` + + +:func:`~collections.namedtuple`-style dynamic class generation +============================================================== + +- See `namedbidict's implementation + `_ + + +How to efficiently implement an ordered mapping +=============================================== + +- Use a backing dict and doubly-linked list + `like OrderedDict + `_ + +- See `OrderedBidict's implementation + `_ API Design ========== -- Making APIs "Pythonic" +- Integrating with :mod:`collections` via :mod:`collections.abc` and :mod:`abc` - - `Zen of Python `_ + - Extending :class:`collections.abc.Mapping` and :class:`collections.abc.MutableMapping` + + - How to make virtual subclasses using + :meth:`abc.ABCMeta.register` or + :meth:`abc.ABCMeta.__subclasshook__` and + :obj:`NotImplemented`. + +- Beyond :class:`collections.abc.Mapping`, bidicts implement additional APIs + that :class:`dict` and :class:`~collections.OrderedDict` implement. + + - When creating a new API, making it familiar, memorable, and intuitive + is hugely important to a good user experience. + +- Making APIs Pythonic + + - `Zen of Python `_ - "Errors should never pass silently. Unless explicitly silenced. @@ -31,73 +113,31 @@ API Design - "Explicit is better than implicit. There should be one—and preferably only one—obvious way to do it." → dropped the alternate ``.inv`` APIs that used - the ``~`` operator and the slice syntax hack - -- Integrating with :mod:`collections` via :mod:`collections.abc` and :mod:`abc` - - - Extending :class:`collections.abc.Mapping` and :class:`collections.abc.MutableMapping` - - - Using :meth:`abc.ABCMeta.register`, - :meth:`abc.ABCMeta.__subclasshook__`, and - :obj:`NotImplemented`. - -- Beyond :class:`collections.abc.Mapping`, bidicts implement as much of the - :class:`dict` and :class:`~collections.OrderedDict` APIs as possible. - When working with APIs, familiarity and memorability are hugely important. - - -Python's data model -=================== - -- Making an immutable type hashable, i.e. insertable into :class:`dict`\s and :class:`set`\s - (see :meth:`object.__hash__` and :meth:`object.__eq__`), - how that interacts with ordered vs. unordered hashable types - that may compare equal - (e.g. - :class:`~bidict.frozenbidict` and - :class:`~bidict.FrozenOrderedBidict`), - some interesting resulting corner cases - - - :ref:`nan-as-key` - - - equal keys of different type, - intransitive equality (as in :class:`~collections.OrderedDict`): - https://github.com/cosmologicon/pywat/issues/38 - - - "Intransitive equality was a mistake." –Raymond Hettinger - -- Using :meth:`object.__new__` to bypass default object initialization - -- Using :meth:`object.__reduce__` to make an object pickleable that otherwise wouldn't be, - due to e.g. using weakrefs (see below) - -- Overriding :meth:`object.__getattribute__` for custom attribute lookup - (see :ref:`sorted-bidict-recipes` for example) - - -Implementing an ordered mapping using a circular doubly-linked list -=================================================================== - -OrderedDict's -`implementation `_ -is a great reference. + the ``~`` operator and the old slice syntax Portability =========== -- Python 2 vs. Python 3 (:class:`dict` API changes) +- Python 2 vs. Python 3 (mostly :class:`dict` API changes) - CPython vs. PyPy - - gc / weakref differences + - gc / weakref + + - http://doc.pypy.org/en/latest/cpython_differences.html#differences-related-to-garbage-collection-strategies + - hence https://github.com/jab/bidict/blob/958ca85/tests/test_hypothesis.py#L168 + + - nan Correctness, performance, code quality, etc. ============================================ bidict provided a need to learn these fantastic tools, -many of which have been indispensable: +many of which have been indispensable +(especially hypothesis – see +`bidict's usage `_): - `Pytest `_ - `Coverage `_ diff --git a/docs/orderedbidict.rst.inc b/docs/orderedbidict.rst.inc index eb6cb2a..27ba740 100644 --- a/docs/orderedbidict.rst.inc +++ b/docs/orderedbidict.rst.inc @@ -67,6 +67,11 @@ will have its value overwritten in place:: OrderedBidict([(3, 4), (5, 2), (7, 8)]) +.. _eq-order-insensitive: + +:meth:`~bidict.FrozenOrderedBidict.__eq__` is order-insensitive +############################################################### + To ensure that equality of bidicts is transitive, and to comply with the `Liskov substitution principle `_,