Metadata-Version: 2.0
Name: withref
Version: 0.3.0
Summary: Use with to simplify multi-level object dereferences, reminisent of Pascal's with statement
Home-page: https://bitbucket.org/jeunice/withref
Author: Jonathan Eunice
Author-email: jonathan.eunice@gmail.com
License: Apache License 2.0
Keywords: with reference dereference Pascal
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Operating System :: OS Independent
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules


| |version| |downloads| |supported-versions| |supported-implementations| |wheel|  |coverage|

.. |version| image:: http://img.shields.io/pypi/v/withref.svg?style=flat
    :alt: PyPI Package latest release
    :target: https://pypi.python.org/pypi/withref

.. |downloads| image:: http://img.shields.io/pypi/dm/withref.svg?style=flat
    :alt: PyPI Package monthly downloads
    :target: https://pypi.python.org/pypi/withref

.. |supported-versions| image:: https://img.shields.io/pypi/pyversions/withref.svg
    :alt: Supported versions
    :target: https://pypi.python.org/pypi/withref

.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/withref.svg
    :alt: Supported implementations
    :target: https://pypi.python.org/pypi/withref

.. |wheel| image:: https://img.shields.io/pypi/wheel/withref.svg
    :alt: Wheel packaging support
    :target: https://pypi.python.org/pypi/withref

.. |coverage| image:: https://img.shields.io/badge/test_coverage-100%25-6600CC.svg
    :alt: Test line coverage
    :target: https://pypi.python.org/pypi/withref


``withref`` makes Python's ``with`` statement able to simplify complex
dereferences. This is what I initially, naively thought the statement was
for, similar to the ``with`` statement of half-remembered Pascal.

The typical Python use is a more complex guard over the entry and exit
points for using an object. See e.g. `this effbot article
<http://effbot.org/zone/python-with-statement.htm>`_, But ``withref`` makes
the simpler "just give me the dereferenced object, please" use case work as
well.

Usage
=====

::

    from withref import ref
    from stuf import stuf

    # stuf used for demonstration purposes only. Convenient way to
    # make dict entries accessible as dot-deferenceable properties.

    a = stuf({ 'b': { 'c': { 'c1': 1 }, 'd': 44.1 } })

    with ref(a.b.c) as c:
        c.c1 = 99

    print a
    a_ideal = stuf({'b': {'c': {'c1': 99}, 'd': 44.1}})
    assert a == a_ideal

It works with array-style references too, of course::

    with ref(a['b']['c']) as cc:
        c['c1'] = 99

    assert a == a_ideal

But beware OVER-dereferencing! While this works correctly::

    with ref(a.b.c.c1) as c1:
        print c1
        assert c1 == 99

This does NOT::

    with ref(a.b.c.c1) as c1:
        c1 = 12345
        assert c1 == 12345

    assert a.b.c.c1 == 99   # with heavy heart
                            # because we just ostensibly set it to 12345

Unlike Pascal, ``ref`` is not built into the language proper. And unlike Perl,
Python is less eager to provide `lvalues
<https://en.wikipedia.org/wiki/Value_(computer_science)#lrvalue>`_ for every
mention of a variable or value. As a result, ``ref`` can provide the *value*
of the full-deferenced ``c1``, but not the assignable *lvalue*.

Like Pascal's ``with``, ``ref`` removes N-1 layers of the enclosing structure
for values, but only N-2 for assignment. Still, in complex multi-layer
structures, this can be a nice simplification::

    with ref(app.config.server.wsgi) as wsgi:
        wsgi.logger = some_logger
        wsgi.debug_level = 4
        wsgi.port = 8080

arguably beats::

    app.config.server.wsgi.logger = some_logger
    app.config.server.wsgi.debug_level = 4
    app.config.server.wsgi.port = 8080

for simplicity and clarity.

I see a lot of configuration code that constantly repeats the same long
multi-level dereferences. That style is repetitive (anti-DRY) and tends to
left-align code blocks, both of which impede program comprehension. Using the
``with`` statement is a neat way to simplify and at the same time add a little extra
visual structure.

And while assignment ("lvalue production") isn't always possible, there still are
some interesting tricks possible with simple value production::

    with ref("this is a string"[0:4]) as t:
        print t

Alternative
===========

While ``yadda.yadda.yadda`` referencing is all too common, one can also
use a more proximate variable assignment::

    wsgi = app.config.server.wsgi
    wsgi.logger = some_logger
    wsgi.debug_level = 4
    wsgi.port = 8080

This lacks the more indented structure of the ``withref`` approach, but
is still much preferable to what you often find in the field.

See Also
========

The `withhacks <http://pypi.python.org/pypi/withhacks>`_ module, which includes
many other fun hacks (multi-line lambdas, new looping structures, etc.)--but
also requires the `byteplay <http://pypi.python.org/pypi/byteplay>`_ module which
actively introspects & munges Python bytecode ("Danger, Will Robinson!
Danger!"), and has not been updated to work beyond Python 2.6.

Possible Future Extension
=========================

As ``withhacks`` shows, using introspection we could determine the lvalue of
the calling object even in the edge-case where it is a leaf-node of its
enclosing structure. This would not require any bytecode changes, and should
be compatible with modern versions of Python (e.g. 2.7.x and 3.x).

Whether that trick can be done simply, portably, rock-solid reliably, and
transparently enough to satisfy those who code the modules that tend to most
need this kind of dereferencing simplification--i.e. complex modules often
used in production settings, into which they are understandably loathe to
introduce any possible sources of error or any performance impedance--that
is the key open question.

Notes
=====

* Version 0.3.0 integrates coverage testing and achieves 100% coverage.

* Version 0.2.3 adds wheel package support.

* Version 0.2.2 switches from BSD to Apache License 2.0.

* Automated multi-version testing managed with `pytest
  <http://pypi.python.org/pypi/pytest>`_, `pytest-cov
  <http://pypi.python.org/pypi/pytest-cov>`_,
  `coverage <https://pypi.python.org/pypi/coverage/4.0b1>`_
  and `tox
  <http://pypi.python.org/pypi/tox>`_.
  Packaging linting with `pyroma <https://pypi.python.org/pypi/pyroma>`_.

  Successfully packaged for, and
  tested against, all late-model versions of Python: 2.6, 2.7, 3.2, 3.3,
  3.4, and 3.5 pre-release (3.5.0b3) as well as PyPy 2.6.0 (based on
  2.7.9) and PyPy3 2.4.0 (based on 3.2.5). Test line coverage 100%.

* The author, `Jonathan Eunice <mailto:jonathan.eunice@gmail.com>`_ or
  `@jeunice on Twitter <http://twitter.com/jeunice>`_
  welcomes your comments and suggestions.

Installation
============

To install or upgrade to the latest version::

    pip install -U withref

To ``easy_install`` under a specific Python version (3.3 in this example)::

    python3.3 -m easy_install --upgrade withref

(You may need to prefix these with ``sudo`` to authorize
installation. In environments without super-user privileges, you may want to
use ``pip``'s ``--user`` option, to install only for a single user, rather
than system-wide.)


