You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

pyrsistent-mutable

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pyrsistent-mutable

Decorator to create and update immutable values with imperative syntax.

0.0.6
pipPyPI
Maintainers
1

Imperative modifications of immutable collections


Overview
========

The `pyrsistent-mutable` package presents a decorator that will transform a decorated function to use the
`pyrsistent <https://pypi.python.org/pypi/pyrsistent>`_ API.

This means that a set of specific operations are transformed:

* Construction of literal ``set``\s, ``dict``\s and ``list``\s are transformed into calls to ``pset``\, ``pvector``
  and ``pmap``\.
* Assignments are rewritten to handle:
    * Assignments to attributes become evolve calls; nesting is handled correctly.
    * Augmented assignments are transformed into regular assignments.
* Standalone method invocations are transformed into assignments.

.. code-block:: python

    from pyrsistent_mutable import pyrmute
    from pyrsistent import PRecord, field

    class Simple(PRecord):
        attr = field()
        other = field()

    @pyrmute
    def example_func():
        # Built in referential integrity
        save_vector = my_vector = [0, 1, 2, 3, 4]  # Mapped to a pvector
        del my_vector[3]  # Does *not* change save_vector

        # Evolve nested attributes
        my_precord = Simple(attr=Simple(), other=[])
        my_precord.attr.attr = 5
        my_precord.other.append(20)

        # Transforms literals and comprehensions
        my_maps = [{'filling': key} for key in ('apple', 'banana')]
        my_maps[0]['crust'] = 'flaky'

        return my_vector, save_vector, my_precord, my_maps

*This example is tested in* ``tests/test_readme.py``\.

What's the point?
-----------------

It's entirely that the imperative form is easier to read, and that pyrsistent's API is tedious for nested collections,
at least compared to native Python syntax.

Also, I'm working on a language that uses this technique more extensively, so this was an opportunity to turn a
prototype into something more generally useful.

Installing
----------

Installation should just be:

.. code-block:: bash

    # Install via pip, preferred.
    pip3 install pyrsistent-mutable

    # Install traditionally.
    python3 setup.py install

Usage
-----

Beyond the example shown above, the main things to keep in mind when using this module:

* You function still needs to return values.
* A "copy" can be made by simple assignment.
* Lists, dicts and sets literals and comprehensions are transformed.
* Tuples are *not* transformed, nor are generators.
* Method calls are *only* transformed if they are standalone expressions.
* Rewritten operations should fall back to normal behavior for non-`pyrsistent` values.
* The decorated function can't allow ``nonlocal`` names.
* ``global`` may not work.

Troubleshooting
===============

This is really just trying to take a prototype and do something useful with it.

If a function isn't calling something in a useful manner, the culprit is probably my very lame implementations in
``pyrsistent_mutable.globals``.

Don't forget to ``return``
--------------------------

This only munges assignments and expression statements.

Read the ``__source__``
-----------------------

The transformed code is written into your function under ``__source__`` which may be helpful in debugging.

Known limits
------------

Most of these are because I've only done very preliminary work to map imperative operations to pyrsistent values.

* Assignment of slices uses the evolver framework, which doesn't handle complex slices.
* Deletion of slices similarly doesn't work.
* Augmented assignment generally requires a pyrsistent value on the rhs.
    * This is mitigated now that the module translates literals.
* It is not tested on asynchronous functions or generators. It shouldn't care about them, though.
* It's all or nothing.
* The top level function can't have ``nonlocal`` names. Embedded functions can, though.

Debugging
---------

By default, the decorator will write the transformed source to your function as ``__source__``\. I just pulled that name
out my hat. You can call the decorator with ``write_source=False`` to disable this.

Package maintainer notes
========================

.. code-block:: bash

    pip install twine
    python setup.py bdist_wheel
    twine upload dist/pyrsistent_mutable-0.0.x-py3-none-any.whl


FAQs

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts