Skip to content

Commit

Permalink
🎨 style: use pycon
Browse files Browse the repository at this point in the history
  • Loading branch information
nstarman committed Dec 4, 2024
1 parent cedab96 commit 95f3bb5
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 8 deletions.
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface for object manipulation. 🕶️

For example,

```pycon
```python
from dataclassish import replace # New object, replacing select fields

d1 = {"a": 1, "b": 2.0, "c": "3"}
Expand Down Expand Up @@ -79,6 +79,7 @@ Point(x=1.0, y=2.0)
>>> p2 = replace(p, x=3.0)
>>> p2
Point(x=3.0, y=2.0)

```

#### Replacing a `dict`
Expand All @@ -104,6 +105,7 @@ objects, but with `dataclassish` it's easy!
... except ValueError as e:
... print(e)
invalid keys {'z'}.

```

#### Replacing via the `__replace__` Method
Expand Down Expand Up @@ -167,6 +169,7 @@ MyClass(a=1,b=2,c=3)
>>> obj2 = replace(obj, c=4.0)
>>> obj2
MyClass(a=1,b=2,c=4.0)

```

### Nested Replacement
Expand All @@ -177,13 +180,15 @@ Point objects:

```pycon
>>> p = {"a": Point(1, 2), "b": Point(3, 4), "c": Point(5, 6)}

```

With `replace` the nested structure can be updated via:

```pycon
>>> replace(p, {"a": {"x": 1.5}, "b": {"y": 4.5}, "c": {"x": 5.5}})
{'a': Point(x=1.5, y=2), 'b': Point(x=3, y=4.5), 'c': Point(x=5.5, y=6)}

```

In contrast in pure Python this would be very challenging. Expand the example
Expand All @@ -201,6 +206,7 @@ This is a bad approach, updating the frozen dataclasses in place:
>>> object.__setattr__(newp["a"], "x", 1.5)
>>> object.__setattr__(newp["b"], "y", 4.5)
>>> object.__setattr__(newp["c"], "x", 5.5)

```

A better way might be to create an entirely new object!
Expand All @@ -209,6 +215,7 @@ A better way might be to create an entirely new object!
>>> newp = {"a": Point(1.5, p["a"].y),
... "b": Point(p["b"].x, 4.5),
... "c": Point(5.5, p["c"].y)}

```

This isn't so good either.
Expand Down Expand Up @@ -242,6 +249,7 @@ Collection(a=Object(x=1.0, y=2.0), b=Object(x=3.0, y=4.0))

>>> replace(p, {"a": {"x": 5.0}, "b": {"y": 6.0}})
Collection(a=Object(x=5.0, y=2.0), b=Object(x=3.0, y=6.0))

```

With `replace` this remains a one-liner. Replace pieces of any structure,
Expand All @@ -255,6 +263,7 @@ To disambiguate dictionary fields from nested structures, use the `F` marker.
>>> replace(p, {"a": {"x": F({"thing": 5.0})}})
Collection(a=Object(x={'thing': 5.0}, y=2.0),
b=Object(x=3.0, y=4.0))

```

### dataclass tools
Expand All @@ -276,6 +285,7 @@ these functions.

>>> astuple(p)
(1.0, 2.0)

```

`dataclassish` extends these functions to [`dict`][dict-link]'s:
Expand All @@ -291,6 +301,7 @@ these functions.

>>> astuple(p)
(1, 2.0)

```

Support for custom objects can be implemented similarly to `replace`.
Expand Down Expand Up @@ -321,6 +332,7 @@ utilities.

>>> field_items(p)
(('x', 1.0), ('y', 2.0))

```

These functions work on any object that has been registered in, not just
Expand All @@ -340,6 +352,7 @@ dict_values([1, 2.0])

>>> field_items(p)
dict_items([('x', 1), ('y', 2.0)])

```

### Converters
Expand Down Expand Up @@ -380,6 +393,7 @@ None
>>> obj = Class2("1")
>>> obj.attr
1.0

```

### Flags
Expand All @@ -395,6 +409,7 @@ consideration by the functions in `dataclassish`.
>>> from dataclassish import flags
>>> flags.__all__
['FlagConstructionError', 'AbstractFlag', 'NoFlag']

```

Where `AbstractFlag` is the base class for flags, and `NoFlag` is a flag that
Expand All @@ -407,6 +422,7 @@ As a quick example, we'll show how to use `NoFlag`.
>>> from dataclassish import field_keys
>>> tuple(field_keys(flags.NoFlag, p))
('x', 'y')

```

## Citation
Expand Down
21 changes: 14 additions & 7 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,30 @@

from sybil import Document, Region, Sybil
from sybil.parsers.myst import (
DocTestDirectiveParser as MarkdownDocTestDirectiveParser,
PythonCodeBlockParser as MarkdownPythonCodeBlockParser,
SkipParser as MarkdownSkipParser,
DocTestDirectiveParser as MystDocTestDirectiveParser,
PythonCodeBlockParser as MystPythonCodeBlockParser,
SkipParser as MystSkipParser,
)
from sybil.parsers.rest import DocTestParser as ReSTDocTestParser
from sybil.sybil import SybilCollection

optionflags = ELLIPSIS | NORMALIZE_WHITESPACE

parsers: Sequence[Callable[[Document], Iterable[Region]]] = [
MarkdownDocTestDirectiveParser(optionflags=optionflags),
MarkdownPythonCodeBlockParser(doctest_optionflags=optionflags),
MarkdownSkipParser(),
MystDocTestDirectiveParser(optionflags=optionflags),
MystPythonCodeBlockParser(doctest_optionflags=optionflags),
MystSkipParser(),
]

# TODO: figure out native parser for `pycon` that doesn't require a new line at
# the end.
readme = Sybil(
parsers=[ReSTDocTestParser(optionflags=optionflags)],
patterns=["README.md"],
)
docs = Sybil(parsers=parsers, patterns=["*.md"])
python = Sybil(
parsers=[ReSTDocTestParser(optionflags=optionflags), *parsers], patterns=["*.py"]
)

pytest_collect_file = (docs + python).pytest()
pytest_collect_file = SybilCollection([docs, readme, python]).pytest()

0 comments on commit 95f3bb5

Please sign in to comment.