Skip to content

Commit

Permalink
Merge pull request #257 from jacebrowning/disable-hooks
Browse files Browse the repository at this point in the history
Add public API for disabling hooks
  • Loading branch information
jacebrowning committed Feb 24, 2022
2 parents db57256 + ad3d8b6 commit c0b6d3f
Show file tree
Hide file tree
Showing 23 changed files with 186 additions and 158 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,7 @@
# 1.2 (2022-02-24)

- Added a `frozen()` context manager to temporarily disable file updates.

# 1.1.1 (2022-02-02)

- Fixed handling of `OSError` when trying to determine a models path.
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Expand Up @@ -54,7 +54,7 @@ $ make docs
Keep all of the above tasks running on change:

```text
$ make watch
$ make dev
```

> In order to have OS X notifications, `brew install terminal-notifier`.
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Expand Up @@ -9,8 +9,8 @@ all: install
.PHONY: ci
ci: format check test mkdocs ## Run all tasks that determine CI status

.PHONY: watch
watch: install ## Continuously run all CI tasks when files chanage
.PHONY: dev
dev: install ## Continuously run all CI tasks when files chanage
poetry run ptw

.PHONY: demo
Expand Down
1 change: 1 addition & 0 deletions datafiles/__init__.py
Expand Up @@ -4,5 +4,6 @@

from . import converters, settings
from .decorators import auto, datafile
from .hooks import disabled as frozen
from .manager import Missing
from .model import Model
9 changes: 9 additions & 0 deletions datafiles/tests/test_hooks.py
Expand Up @@ -2,6 +2,7 @@

from typing import List

import datafiles
from datafiles import datafile, field, hooks, settings


Expand Down Expand Up @@ -48,3 +49,11 @@ def when_nested(expect):
expect(settings.HOOKS_ENABLED).is_(False)

expect(settings.HOOKS_ENABLED).is_(True)

def with_alias(expect):
expect(settings.HOOKS_ENABLED).is_(True)

with datafiles.frozen():
expect(settings.HOOKS_ENABLED).is_(False)

expect(settings.HOOKS_ENABLED).is_(True)
42 changes: 21 additions & 21 deletions docs/api/manager.md
Expand Up @@ -19,55 +19,55 @@ Many of the following examples are also shown in this [Jupyter Notebook](https:/
Instantiate an object from an existing file. If no matching file exist, or if any other problem occurs, an appropriate exception will be raised.

```python
>>> MyModel.objects.get('foobar')
>>> MyModel.objects.get("foobar")
Traceback (most recent call last):
...
FileNotFoundError: [Errno 2] No such file or directory: 'foobar.yml'
FileNotFoundError: [Errno 2] No such file or directory: "foobar.yml"
```

```python
>>> m = MyModel('foobar', 42)
>>> m = MyModel("foobar", 42)
```

```python
>>> MyModel.objects.get('foobar')
MyModel(my_key='foobar', my_value=42)
>>> MyModel.objects.get("foobar")
MyModel(my_key="foobar", my_value=42)
```

## `get_or_none()`

Instantiate an object from an existing file or return `None` if no matching file exists:

```python
>>> MyModel.objects.get_or_none('foobar')
>>> MyModel.objects.get_or_none("foobar")
None
```

```python
>>> m = MyModel('foobar', 42)
>>> m = MyModel("foobar", 42)
```

```python
>>> MyModel.objects.get_or_none('foobar')
MyModel(my_key='foobar', my_value=42)
>>> MyModel.objects.get_or_none("foobar")
MyModel(my_key="foobar", my_value=42)
```

## `get_or_create()`

Instantiate an object from an existing file or create one if no matching file exists:

```python
>>> m = MyModel('foo', 42)
>>> m = MyModel("foo", 42)
```

```python
>>> MyModel.objects.get_or_create('foo')
MyModel(my_key='foo', my_value=42)
>>> MyModel.objects.get_or_create("foo")
MyModel(my_key="foo", my_value=42)
```

```python
>>> MyModel.objects.get_or_create('bar')
MyModel(my_key='bar', my_value=0)
>>> MyModel.objects.get_or_create("bar")
MyModel(my_key="bar", my_value=0)
```

## `all()`
Expand All @@ -81,22 +81,22 @@ Iterate over all objects matching the pattern:
```

```python
>>> m1 = MyModel('foo')
>>> m2 = MyModel('bar', 42)
>>> m1 = MyModel("foo")
>>> m2 = MyModel("bar", 42)
```

```python
>>> for m in MyModel.objects.all():
... print(m)
...
MyModel(my_key='foo' my_value=0)
MyModel(my_key='bar', my_value=42)
MyModel(my_key="foo" my_value=0)
MyModel(my_key="bar", my_value=42)
```

Exclude objects from ever being loaded and returned with `_exclude`:

```python
>>> generator = MyModel.objects.all(_exclude='foo')
>>> generator = MyModel.objects.all(_exclude="foo")
```

## `filter()`
Expand All @@ -106,13 +106,13 @@ Iterate all objects matching the pattern with additional required attribute valu
```python
>>> generator = MyModel.objects.filter(my_value=42)
>>> list(generator)
[MyModel(my_key='foo', my_value=42)]
[MyModel(my_key="foo", my_value=42)]
```

Exclude objects from ever being loaded and returned with `_exclude`:

```python
>>> generator = MyModel.objects.filter(_exclude='foo')
>>> generator = MyModel.objects.filter(_exclude="foo")
```

Nested dataclass values can be queried using `__` as a delimiter:
Expand Down
6 changes: 3 additions & 3 deletions docs/api/mapper.md
Expand Up @@ -13,7 +13,7 @@ class MyModel:
```

```python
>>> model = MyModel('foo')
>>> model = MyModel("foo")
```

Many of the following examples are also shown in this [Jupyter Notebook](https://github.com/jacebrowning/datafiles/blob/main/notebooks/mapper_api.ipynb).
Expand All @@ -24,7 +24,7 @@ Get the full path to the mapped file:

```python
>>> model.datafile.path
PosixPath('/Projects/Demo/my_models/foo.yml')
PosixPath("/Projects/Demo/my_models/foo.yml")
```

## `exists`
Expand Down Expand Up @@ -77,5 +77,5 @@ Access the parsed model attributes directly:

```python
>>> model.datafile.data
ordereddict([('my_value', 1)])
ordereddict([("my_value", 1)])
```
4 changes: 2 additions & 2 deletions docs/api/model.md
Expand Up @@ -103,7 +103,7 @@ class Item:
available: bool

class Meta:
datafile_attrs = {'count': converters.Integer}
datafile_attrs = {"count": converters.Integer}
datafile_manual = True
datafile_defaults = True
```
Expand All @@ -125,7 +125,7 @@ class Item(Model):

class Meta:
datafile_pattern = "items/{self.name}.yml"
datafile_attrs = {'count': converters.Integer}
datafile_attrs = {"count": converters.Integer}
datafile_manual = True
datafile_defaults = True
```
6 changes: 3 additions & 3 deletions docs/formats.md
Expand Up @@ -97,7 +97,7 @@ To map one of the existing formatter classes to a new file extension:
from datafile import datafile, formats


formats.register('.conf', formats.YAML)
formats.register(".conf", formats.YAML)


@datafile("my-file-path.conf")
Expand All @@ -116,7 +116,7 @@ class MyFormat(formats.Format):

@classmethod
def extensions(cls) -> list[str]:
return ['.my_ext']
return [".my_ext"]

@classmethod
@abstractmethod
Expand All @@ -129,7 +129,7 @@ class MyFormat(formats.Format):
# Convert `data` to a string


formats.register('.my_ext', MyFormat)
formats.register(".my_ext", MyFormat)


@datafile("my-file-path.my_ext")
Expand Down
4 changes: 2 additions & 2 deletions docs/types/builtins.md
Expand Up @@ -38,6 +38,6 @@ from typing import Optional
| Type Annotation | Python Value | YAML Content |
| ----------------------- | -------------------------- | ----------------------- |
| `foobar: str` | `foobar = "Hello, world!"` | `foobar: Hello, world!` |
| `foobar: str` | `foobar = 42` | `foobar: '42'` |
| `foobar: str` | `foobar = None` | `foobar: ''` |
| `foobar: str` | `foobar = 42` | `foobar: "42"` |
| `foobar: str` | `foobar = None` | `foobar: ""` |
| `foobar: Optional[str]` | `foobar = None` | `foobar:` |
2 changes: 1 addition & 1 deletion docs/types/containers.md
Expand Up @@ -45,7 +45,7 @@ from typing import Dict, Optional
| Type Annotation | Python Value | YAML Content |
| ---------------------------------- | -------------------- | -------------------------------------------- |
| `foobar: Dict[str, int]` | `foobar = {}` | `foobar: {}` |
| `foobar: Dict[str, int]` | `foobar = {'a': 42}` | `foobar:`<br>&nbsp;&nbsp;&nbsp;&nbsp;`a: 42` |
| `foobar: Dict[str, int]` | `foobar = {"a": 42}` | `foobar:`<br>&nbsp;&nbsp;&nbsp;&nbsp;`a: 42` |
| `foobar: Dict[str, int]` | `foobar = None` | `foobar: {}` |
| `foobar: Optional[Dict[str, int]]` | `foobar = None` | `foobar:` |

Expand Down
2 changes: 1 addition & 1 deletion docs/types/extensions.md
Expand Up @@ -33,7 +33,7 @@ from datafiles.converters import Text
| ------------------------ | ---------------------------- | ---------------------------------------------------------------------------------- |
| `foobar: Text` | `foobar = "Hello, world!"` | `foobar: Hello, world!` |
| `foobar: Text` | `foobar = "First\nSecond\n"` | `foobar: |`<br>&nbsp;&nbsp;&nbsp;&nbsp;`First`<br>&nbsp;&nbsp;&nbsp;&nbsp;`Second` |
| `foobar: Text` | `foobar = None` | `foobar: ''` |
| `foobar: Text` | `foobar = None` | `foobar: ""` |
| `foobar: Optional[Text]` | `foobar = None` | `foobar:` |

## Enumerations
Expand Down
26 changes: 20 additions & 6 deletions docs/utilities.md
Expand Up @@ -19,9 +19,9 @@ can be loaded into an object:

```python
>>> from datafiles import auto
>>> sample = auto('sample.yml')
>>> sample = auto("sample.yml")
>>> sample.names
['Alice', 'Bob']
["Alice", "Bob"]
```

where modified attributes:
Expand All @@ -32,10 +32,7 @@ where modified attributes:

are automatically reflected in the file:

```
#!text hl_lines="9"
$ cat sample.yml
```yaml hl_lines='7'
names:
- Alice
- Bob
Expand All @@ -46,3 +43,20 @@ numbers:
```

Additional examples can be found in this [Jupyter Notebook](https://github.com/jacebrowning/datafiles/blob/main/notebooks/file_inference.ipynb).

## `frozen()`

This context manager can be used to temporarily disable saving objects to the filesystem:

```python
import datafiles

from .models import MyModel

instance = MyModel()

with datafiles.frozen():
instance.value = 42
```

Additional modifications to the object will synchronize all changes.

0 comments on commit c0b6d3f

Please sign in to comment.