# odatadol.store

A read-only `Mapping` over one OData entity set, keyed on a declared key.

[`ODataReader`](#odatadol.store.ODataReader) is a thin facade over [`odatadol.read`](odatadol.read.html.md#module-odatadol.read): `len` is one
`$count`, iterating reads only the key column, `store[k]` and `k in store` are one
filtered request each, and `items()`/`values()` read in bulk (column groups joined on
the key when `select` is wide), never one request per key.

```pycon
>>> from odatadol.testing import FakeService
>>> server = FakeService([{'ProductID': 1, 'Name': 'Tea'}, {'ProductID': 2, 'Name': 'Jam'}])
>>> store = ODataReader('https://example.com/odata', 'Products', key='ProductID',
...                     fetch=server)
>>> len(store), list(store), store[2]
(2, [1, 2], {'ProductID': 2, 'Name': 'Jam'})
>>> 3 in store
False
```

Writes are not here, and not behind a flag: a writer, if one is ever needed, is a
separate class, so code holding a reader cannot reach one by passing the wrong argument.

### Functions

| [`odata_literal`](#odatadol.store.odata_literal)(value)   | `value` written as an OData `$filter` literal.   |
|-------------------------------------------------------------------------|--------------------------------------------------|

### Classes

| [`ODataReader`](#odatadol.store.ODataReader)(service, entity, \*, key[, ...])   | The records of `entity` at `service`, as `{key value: record}`.   |
|-------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|

### *class* odatadol.store.ODataReader(service, entity, , key, select=None, filter=None, version=None, credentials=None, fetch=None, max_url=2000, page_size=1000, parse_float=None, literal=None)

Bases: [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)

The records of `entity` at `service`, as `{key value: record}`.

* **Parameters:**
  * **service** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The OData service root URL.
  * **entity** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The entity set name.
  * **key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The property records are addressed, ordered and joined by. It must be
    unique and never empty within the reader’s `filter`.
  * **select** ([`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – The properties a record holds. `None` fetches all of them; a wide
    list is split into column groups under `max_url` for bulk reads.
  * **filter** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – A `$filter` scoping the reader: keys outside it are not in it.
  * **version** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – A property rewritten on every edit, checked per key across column
    groups so a bulk read is one moment (see
    [`odatadol.read.entity_columns()`](odatadol.read.html.md#odatadol.read.entity_columns)).
  * **credentials** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`None`](https://docs.python.org/3/builtins/constants.html#None), `_HasHeader`, [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]]) – `None`, a [`BasicAuth`](odatadol.transport.html.md#odatadol.transport.BasicAuth) or
    [`BearerToken`](odatadol.transport.html.md#odatadol.transport.BearerToken), or a callable returning one.
  * **fetch** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]], [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`int`](https://docs.python.org/3/builtins/functions.html#int), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – The transport, `(url, headers) -> (status, bytes)`.
  * **literal** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)], [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Writes a key as a `$filter` literal, for `store[k]` and
    `k in store`. Defaults to [`odata_literal()`](#odatadol.store.odata_literal); an `Edm.Guid` key read
    back as text by iteration needs `literal=str`. Raises `TypeError` for a
    key it cannot write, which the reader reports as missing.
  * **parse_float** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Applied to fractional JSON numbers (`decimal.Decimal` keeps
    every digit).

#### items()

Every `(key, record)`, read in bulk.

* **Return type:**
  [`ItemsView`](https://docs.python.org/3/library/collections.abc.html#collections.abc.ItemsView)

#### values()

Every record, read in bulk.

* **Return type:**
  [`ValuesView`](https://docs.python.org/3/library/collections.abc.html#collections.abc.ValuesView)

### odatadol.store.odata_literal(value)

`value` written as an OData `$filter` literal.

```pycon
>>> odata_literal("O'Brien"), odata_literal(42), odata_literal(True)
("'O''Brien'", '42', 'true')
>>> odata_literal(float('nan')), odata_literal(Decimal('1E+2'))
('NaN', '100')
>>> odata_literal(uuid.UUID(int=1)), odata_literal(datetime.date(2026, 1, 2))
('00000000-0000-0000-0000-000000000001', '2026-01-02')
```

A `str` is always a quoted string literal: pass a [`uuid.UUID`](https://docs.python.org/3/library/uuid.html#uuid.UUID) for an
`Edm.Guid` and a [`datetime`](https://docs.python.org/3/library/datetime.html#module-datetime) object for a date or time.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
