mwchase/python-structured-data

View on GitHub
README.rst

Summary

Maintainability
Test Coverage
========
Overview
========

.. image:: http://unmaintained.tech/badge.svg
  :target: http://unmaintained.tech
  :alt: No Maintenance Intended

.. start-badges

.. list-table::
    :stub-columns: 1

    * - docs
      - |docs|
    * - tests
      - | |travis| |appveyor| |requires|
        | |codecov|
        | |codacy| |codebeat| |codeclimate| |scrutinizer| |codefactor|
    * - package
      - | |version| |wheel| |supported-versions| |supported-implementations|
        | |commits-since|

.. |docs| image:: https://readthedocs.org/projects/python-structured-data/badge/?style=flat
    :target: https://readthedocs.org/projects/python-structured-data
    :alt: Documentation Status

.. |travis| image:: https://api.travis-ci.org/mwchase/python-structured-data.svg?branch=master
    :alt: Travis-CI Build Status
    :target: https://travis-ci.org/mwchase/python-structured-data

.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/mwchase/python-structured-data?branch=master&svg=true
    :alt: AppVeyor Build Status
    :target: https://ci.appveyor.com/project/mwchase/python-structured-data

.. |requires| image:: https://requires.io/github/mwchase/python-structured-data/requirements.svg?branch=master
    :alt: Requirements Status
    :target: https://requires.io/github/mwchase/python-structured-data/requirements/?branch=master

.. |codecov| image:: https://codecov.io/gh/mwchase/python-structured-data/branch/master/graphs/badge.svg?branch=master
    :alt: Coverage Status
    :target: https://codecov.io/github/mwchase/python-structured-data

.. |codacy| image:: https://api.codacy.com/project/badge/Grade/1a9e4a5640b446768c21a87d3566d33e
    :target: https://www.codacy.com/app/max-chase/python-structured-data?utm_source=github.com&utm_medium=referral&utm_content=mwchase/python-structured-data&utm_campaign=Badge_Grade

.. |codebeat| image:: https://codebeat.co/badges/de1fa625-e4d4-4e11-bf94-ee9b4a0acf91
    :target: https://codebeat.co/projects/github-com-mwchase-python-structured-data-master
    :alt: Codebeat Code Quality Status

.. |codeclimate| image:: https://api.codeclimate.com/v1/badges/535f66af50b72ee28e62/maintainability
    :target: https://codeclimate.com/github/mwchase/python-structured-data
    :alt: Code Climate Maintainability Score

.. |scrutinizer| image:: https://scrutinizer-ci.com/g/mwchase/python-structured-data/badges/quality-score.png?b=master
    :target: https://scrutinizer-ci.com/g/mwchase/python-structured-data/?branch=master
    :alt: Scrutinizer Code Quality Status

.. |codefactor| image:: https://www.codefactor.io/repository/github/mwchase/python-structured-data/badge
   :target: https://www.codefactor.io/repository/github/mwchase/python-structured-data
   :alt: CodeFactor

.. |version| image:: https://img.shields.io/pypi/v/structured-data.svg
    :alt: PyPI Package latest release
    :target: https://pypi.org/project/structured-data/

.. |commits-since| image:: https://img.shields.io/github/commits-since/mwchase/python-structured-data/v0.13.0.svg
    :alt: Commits since latest release
    :target: https://github.com/mwchase/python-structured-data/compare/v0.13.0...master

.. |wheel| image:: https://img.shields.io/pypi/wheel/structured-data.svg
    :alt: PyPI Wheel
    :target: https://pypi.org/project/structured-data/

.. |supported-versions| image:: https://img.shields.io/pypi/pyversions/structured-data.svg
    :alt: Supported versions
    :target: https://pypi.org/project/structured-data/

.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/structured-data.svg
    :alt: Supported implementations
    :target: https://pypi.org/project/structured-data/


.. end-badges

Code generators for immutable structured data, including algebraic data types, and functions to destructure them.
Structured Data provides three public modules: ``structured_data.adt``, ``structured_data.match``, and ``structured_data.data``.

The ``adt`` module provides base classes and an annotation type for converting a class into algebraic data types.

The ``match`` module provides a ``Pattern`` class that can be used to build match structures, and a ``Matchable`` class that wraps a value, and attempts to apply match structures to it.
If the match succeeds, the bindings can be extracted and used.
It includes some special support for ``adt`` subclasses.

The match architecture allows you tell pull values out of a nested structure:

.. code-block:: python3

    structure = (match.pat.a, match.pat.b[match.pat.c, match.pat.d], 5)
    my_value = (('abc', 'xyz'), ('def', 'ghi'), 5)
    matchable = match.Matchable(my_value)
    if matchable(structure):
        # The format of the matches is not final.
        print(matchable['a'])  # ('abc', 'xyz')
        print(matchable['b'])  # ('def', 'ghi')
        print(matchable['c'])  # 'def'
        print(matchable['d'])  # 'ghi'

The subscript operator allows binding both the outside and the inside of a structure.
Indexing a ``Matchable`` is forwarded to a ``matches`` attribute, which is ``None`` if the last match was not successful, and otherwise contains an instance of a custom mapping type, which allows building the matched values back up into simple structures.

The ``Sum`` base class exists to create classes that do not necessarily have a single fixed format, but do have a fixed set of possible formats.
This lowers the maintenance burden of writing functions that operate on values of a ``Sum`` class, because the full list of cases to handle is directly in the class definition.

Here are implementations of common algebraic data types in other languages:

.. code-block:: python3

    class Maybe(adt.Sum, typing.Generic[T]):

        Just: adt.Ctor[T]
        Nothing: adt.Ctor


    class Either(adt.Sum, typing.Generic[E, R]):

        Left: adt.Ctor[E]
        Right: adt.Ctor[R]

The ``data`` module provides classes based on these examples.

* Free software: MIT license

How Can I Help?
===============

Currently, this project has somewhat high quality metrics, though some of them have been higher.
I am highly skeptical of this, because I've repeatedly given in to the temptation to code to the metrics.
I can't trust the metrics, and I know the code well enough that I can't trust my own judgment to figure out which bits need to be improved and how.
I need someone to review the code and identify problem spots based on what doesn't make sense to them.
The issues are open.

Should I Use This?
==================

Until there's a major version out, probably not.

There are several alternatives in the standard library that may be better suited to particular use-cases:

- The ``namedtuple`` factory creates tuple classes with a single structure; the ``typing.NamedTuple`` class offers the ability to include type information. The interface is slightly awkward, and the values expose their tuple-nature easily. (NOTE: In Python 3.8, the fast access to namedtuple members means that they bypass user-defined ``__getitem__`` methods, thereby allowing factory consumers to customize indexing without breaking attribute access. It looks like it does still rely on iteration behavior for various convenience methods.)
- The ``enum`` module provides base classes to create finite enumerations. Unlike NamedTuple, the ability to convert values into an underlying type must be opted into in the class definition.
- The ``dataclasses`` module provides a class decorator that converts a class into one with a single structure, similar to a namedtuple, but with more customization: instances are mutable by default, and it's possible to generate implementations of common protocols.
- The Structured Data ``adt`` decorator is inspired by the design of ``dataclasses``. (A previous attempt used metaclasses inspired by the ``enum`` module, and was a nightmare.) Unlike ``enum``, it doesn't require all instances to be defined up front; instead each class defines constructors using a sequence of types, which ultimately determines the number of arguments the constructor takes. Unlike ``namedtuple`` and ``dataclasses``, it allows instances to have multiple shapes with their own type signatures. Unlike using regular classes, the set of shapes is specified up front.
- If you want multiple shapes, and don't want to specify them ahead of time, your best bet is probably a normal tree of classes, where the leaf classes are ``dataclasses``.

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

::

    pip install structured-data

Documentation
=============

https://python-structured-data.readthedocs.io/

Development
===========

To run the all tests run::

    nox