Metadata-Version: 2.1
Name: data-records
Version: 0.3.0
Summary: Immutable Data Records with Type Coercion
Home-page: https://gitlab.com/mc706/data-records
Author: Ryan McDevitt
Author-email: mcdevitt.ryan@gmail.com
License: UNKNOWN
Project-URL: Docs, https://data-records.readthedocs.io/en/stable/#
Project-URL: Source, https://gitlab.com/mc706/data-records
Project-URL: Bug Reports/Issues, https://gitlab.com/mc706/data-records/issues
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Utilities
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: prospector ; extra == 'dev'
Requires-Dist: coverage ; extra == 'dev'
Requires-Dist: mypy ; extra == 'dev'
Requires-Dist: pytest ; extra == 'dev'
Requires-Dist: pytest-cov ; extra == 'dev'

# Data Records
[![PyPI version](https://badge.fury.io/py/data-records.svg)](https://badge.fury.io/py/data-records)
[![pipeline status](https://gitlab.com/mc706/data-records/badges/master/pipeline.svg)](https://gitlab.com/mc706/data-records/commits/master)
[![coverage report](https://gitlab.com/mc706/data-records/badges/master/coverage.svg)](https://gitlab.com/mc706/data-records/commits/master)
[![PyPI](https://img.shields.io/pypi/pyversions/data-records.svg)](https://pypi.org/project/data-records/)
[![Documentation Status](https://readthedocs.org/projects/data-records/badge/?version=latest)](https://data-records.readthedocs.io/en/latest/?badge=latest)


In certain Functional languages there is a concept of Records. They are a Product Data Type of immtable data that 
has typed attributes. 

## Goals
The following are the goals and the "north star" for design during the development of this project:

* Ease Of Use
    * Simple Interface
    * Does the obvious thing in most cases
* Immutability
    * Follow Immutability Patterns suchas replace and pattern matching
* Safety
    * Include Type Coersion Where Possible
    * Guarantee that a record has the resulting types
    * Throw Warning when it is implemented Incorrectly

## Motivation

## Enforced Typing
I love `@dataclass`, and was estatic when it was added to python. However certain things like:

```python 
>>> from dataclasses import dataclass, field

>>> @dataclass
... class Foo:
...     bar: str
...     baz: int

>>> Foo(1, 2)
Foo(bar=1, baz=2)

```

are not what I would expect when coming from other typed languages. In statically typed languages, this should throw an 
error because bar should be a string. In languages with type coercion, I would expect that bar would be `"1"`. The default
behavior of dataclasses here does neither, and if I were to use this dataclass somewhere that expected bar to be a sting
it would fail with a runtime exception; exactly what the types were supposed to help prevent.

```python 
>>> from data_records import datarecord

>>> @datarecord
... class Foo:
...     bar: str
...     baz: int

>>> Foo(1, 2)
Foo(bar='1', baz=2)

>>> Foo("a", "b")
Traceback (most recent call last):
 ...
ValueError: invalid literal for int() with base 10: 'b'

```

## Extraneous Field Handling

Another Problem with data classes is trying to pass in a dictionary that has more keys than are required for
creating a dataclass:

```python 
>>> from dataclasses import dataclass

>>> @dataclass
... class Foo:
...     bar: str
...     baz: int

>>> Foo(**{'bar': 'test', 'baz': 1, 'other': 'nothing'})
Traceback (most recent call last):
 ...
TypeError: __init__() got an unexpected keyword argument 'other'

```

This makes it hard to pull data records out of larger database calls or received data.

```python
>>> from data_records import datarecord 

>>> @datarecord
... class Foo:
...     bar: str
...     baz: int

>>> Foo(**{'bar': 'test', 'baz': 1, 'other': 'test'})
Foo(bar='test', baz=1)

```

## Immutable Handling
Data records are immutable (much like frozen dataclasses) and the handling for such is builtin:

```python  
>>> from data_records import datarecord

>>> @datarecord
... class Foo:
...     bar: str
...     baz: int
...     lat: float
...     long: float

>>> example = Foo('test', 2, 65.1, -127.5)
>>> example2 = example.replace(bar='testing')

>>> example
Foo(bar='test', baz=2, lat=65.1, long=-127.5)

>>> example2
Foo(bar='testing', baz=2, lat=65.1, long=-127.5)

>>> latitude, longitude = example.extract('lat', 'long')
>>> latitude
65.1

```


