Skip to content

Commit

Permalink
Merge 7a832ca into 89cd493
Browse files Browse the repository at this point in the history
  • Loading branch information
jacebrowning committed Jan 27, 2019
2 parents 89cd493 + 7a832ca commit d8906f0
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 72 deletions.
16 changes: 8 additions & 8 deletions CONTRIBUTING.md
Expand Up @@ -12,15 +12,15 @@

To confirm these system dependencies are configured correctly:

```sh
```text
$ make doctor
```

## Installation

Install project dependencies into a virtual environment:

```sh
```text
$ make install
```

Expand All @@ -30,27 +30,27 @@ $ make install

Run the tests:

```sh
```text
$ make test
```

Run static analysis:

```sh
```text
$ make check
```

Build the documentation:

```sh
```text
$ make docs
```

## Automatic

Keep all of the above tasks running on change:

```sh
```text
$ make watch
```

Expand All @@ -60,14 +60,14 @@ $ make watch

The CI server will report overall build status:

```sh
```text
$ make ci
```

# Release Tasks

Release to PyPI:

```sh
```text
$ make upload
```
16 changes: 12 additions & 4 deletions datafiles/decorators.py
Expand Up @@ -2,14 +2,16 @@
from typing import Dict, Optional

from .converters import Converter
from .models import create_model
from .models import ModelMeta, create_model


def datafile(
pattern: str,
attrs: Optional[Dict[str, Converter]] = None,
manual: bool = False,
defaults: bool = False,
manual: bool = ModelMeta.datafile_manual,
defaults: bool = ModelMeta.datafile_defaults,
auto_load: bool = ModelMeta.datafile_auto_load,
auto_save: bool = ModelMeta.datafile_auto_save,
):
"""Synchronize a data class to the specified path."""

Expand All @@ -20,7 +22,13 @@ def decorator(cls):
dataclass = dataclasses.dataclass(cls)

return create_model(
dataclass, attrs=attrs, pattern=pattern, manual=manual, defaults=defaults
dataclass,
attrs=attrs,
pattern=pattern,
manual=manual,
defaults=defaults,
auto_load=auto_load,
auto_save=auto_save,
)

return decorator
8 changes: 4 additions & 4 deletions datafiles/hooks.py
Expand Up @@ -83,10 +83,8 @@ def wrapped(self, *args, **kwargs):
if datafile.exists and datafile.modified:
log.debug(f"Loading automatically before '{method.__name__}' call")
datafile.load()
# TODO: Implement this?
# if mapper.auto_save_after_load:
# mapper.save()
# mapper.modified = False
if datafile.auto_save:
datafile.save(_log=False)

return method(self, *args, **kwargs)

Expand Down Expand Up @@ -117,6 +115,8 @@ def wrapped(self, *args, **kwargs):
if enabled(datafile, args):
log.debug(f"Saving automatically after '{method.__name__}' call")
datafile.save()
if datafile.auto_load:
datafile.load(_log=False)

return result

Expand Down
30 changes: 22 additions & 8 deletions datafiles/managers.py
Expand Up @@ -34,6 +34,8 @@ def __init__(
pattern: Optional[str],
manual: bool,
defaults: bool,
auto_load: bool,
auto_save: bool,
root: Optional[InstanceManager] = None,
) -> None:
assert manual is not None
Expand All @@ -43,6 +45,8 @@ def __init__(
self._pattern = pattern
self._manual = manual
self.defaults = defaults
self._auto_load = auto_load
self._auto_save = auto_save
self._last_load = 0.0
self._last_data: Dict = {}
self._root = root
Expand Down Expand Up @@ -95,6 +99,14 @@ def modified(self, modified: bool):
def manual(self) -> bool:
return self._root.manual if self._root else self._manual

@property
def auto_load(self) -> bool:
return self._root.auto_load if self._root else self._auto_load

@property
def auto_save(self) -> bool:
return self._root.auto_save if self._root else self._auto_save

@property
def data(self) -> Dict:
return self._get_data()
Expand Down Expand Up @@ -156,13 +168,14 @@ def _get_text(self, **kwargs) -> str:
return formats.serialize(data, self.path.suffix)
return formats.serialize(data)

def load(self, *, first_load=False) -> None:
def load(self, *, _log=True, _first=False) -> None:
if self._root:
self._root.load(first_load=first_load)
self._root.load(_log=_log, _first=_first)
return

if self.path:
log.info(f"Loading '{self.classname}' object from '{self.relpath}'")
if _log:
log.info(f"Loading '{self.classname}' object from '{self.relpath}'")
else:
raise RuntimeError("'pattern' must be set to load the model")

Expand All @@ -179,7 +192,7 @@ def load(self, *, first_load=False) -> None:
if getattr(converter, 'DATACLASS', None):
self._set_dataclass_value(data, name, converter)
else:
self._set_attribute_value(data, name, converter, first_load)
self._set_attribute_value(data, name, converter, _first)

self.modified = False

Expand Down Expand Up @@ -228,7 +241,7 @@ def _set_attribute_value(self, data, name, converter, first_load):

if first_load:
log.debug(
'First load values: file=%r, init=%r, default=%r',
'Initial load values: file=%r, init=%r, default=%r',
file_value,
init_value,
default_value,
Expand Down Expand Up @@ -263,13 +276,14 @@ def _get_default_field_value(self, name):

return Missing

def save(self, include_default_values: Trilean = None) -> None:
def save(self, *, include_default_values: Trilean = None, _log=True) -> None:
if self._root:
self._root.save(include_default_values=include_default_values)
self._root.save(include_default_values=include_default_values, _log=_log)
return

if self.path:
log.info(f"Saving '{self.classname}' object to '{self.relpath}'")
if _log:
log.info(f"Saving '{self.classname}' object to '{self.relpath}'")
else:
raise RuntimeError(f"'pattern' must be set to save the model")

Expand Down
34 changes: 29 additions & 5 deletions datafiles/models.py
Expand Up @@ -17,6 +17,8 @@ class ModelMeta:

datafile_manual: bool = False
datafile_defaults: bool = False
datafile_auto_load: bool = True
datafile_auto_save: bool = True


class Model:
Expand All @@ -37,7 +39,7 @@ def __post_init__(self):
log.debug(f'Datafile exists: {exists}')

if exists:
self.datafile.load(first_load=True)
self.datafile.load(_first=True)
elif path:
self.datafile.save()

Expand All @@ -59,8 +61,10 @@ def build_datafile(obj, root=None) -> InstanceManager:
m = getattr(obj, 'Meta', None)
pattern = getattr(m, 'datafile_pattern', None)
attrs = getattr(m, 'datafile_attrs', None)
manual = getattr(m, 'datafile_manual', False)
defaults = getattr(m, 'datafile_defaults', False)
manual = getattr(m, 'datafile_manual', ModelMeta.datafile_manual)
defaults = getattr(m, 'datafile_defaults', ModelMeta.datafile_defaults)
auto_load = getattr(m, 'datafile_auto_load', ModelMeta.datafile_auto_load)
auto_save = getattr(m, 'datafile_auto_save', ModelMeta.datafile_auto_save)

if attrs is None and dataclasses.is_dataclass(obj):
attrs = {}
Expand All @@ -71,11 +75,27 @@ def build_datafile(obj, root=None) -> InstanceManager:
attrs[field.name] = map_type(field.type)

return InstanceManager(
obj, attrs=attrs, pattern=pattern, manual=manual, defaults=defaults, root=root
obj,
attrs=attrs,
pattern=pattern,
manual=manual,
defaults=defaults,
auto_load=auto_load,
auto_save=auto_save,
root=root,
)


def create_model(cls, *, attrs=None, pattern=None, manual=None, defaults=None):
def create_model(
cls,
*,
attrs=None,
pattern=None,
manual=None,
defaults=None,
auto_load=None,
auto_save=None,
):
"""Patch datafile attributes on to an existing dataclass."""
log.debug(f'Converting {cls} to a datafile model')

Expand All @@ -95,6 +115,10 @@ def create_model(cls, *, attrs=None, pattern=None, manual=None, defaults=None):
m.datafile_manual = manual
if not hasattr(cls, 'Meta') and defaults is not None:
m.datafile_defaults = defaults
if not hasattr(cls, 'Meta') and auto_load is not None:
m.datafile_auto_load = auto_load
if not hasattr(cls, 'Meta') and auto_save is not None:
m.datafile_auto_save = auto_save

cls.Meta = m

Expand Down
7 changes: 5 additions & 2 deletions datafiles/tests/test_managers.py
Expand Up @@ -6,6 +6,7 @@
import pytest

from datafiles import managers
from datafiles.models import ModelMeta


@dataclass
Expand All @@ -26,8 +27,10 @@ def manager():
instance=MyModel(foobar=42),
attrs={},
pattern=None,
manual=False,
defaults=False,
manual=ModelMeta.datafile_manual,
defaults=ModelMeta.datafile_defaults,
auto_load=ModelMeta.datafile_auto_load,
auto_save=ModelMeta.datafile_auto_save,
)

def describe_path():
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Expand Up @@ -6,13 +6,13 @@ Datafiles is a bidirectional serialization library for Python [dataclasses](http

Install it directly into an activated virtual environment:

```
```text
$ pip install datafiles
```

or add it to your [Poetry](https://poetry.eustace.io/) project:

```
```text
$ poetry add datafiles
```

Expand Down

0 comments on commit d8906f0

Please sign in to comment.