Comments (4)
You mean the defaultdict case will be covered without the hook factory magic?
Yeah, simple cases we can handle automatically. I'm working on a PR already.
I can look at your other comments later, really busy with work ATM.
from cattrs.
Hi,
that's an interesting question. I think nobody's asked for it over the years (or I've forgotten about it); we should probably support it since it's a standard library thing.
First you need a predicate function to see if a type is a defaultdict. Note that on 3.9+ (you're on 3.11) you don't need to use typing.DefaultDict
, you can just use collections.defaultdict
in type hints.
Something like this:
from collections import defaultdict
from typing import DefaultDict, get_origin
def is_defaultdict(type: Any) -> bool:
"""Is this type a defaultdict?"""
return is_subclass(get_origin(type), (defaultdict, DefaultDict))
Now you need a structure hook factory. We can wrap an existing hook factory, cattrs.gen.make_mapping_structure_fn
. You'll need the c
Converter to be in scope.
from functools import partial
from typing import get_args
from cattrs.gen import make_mapping_structure_fn
from cattrs.dispatch import StructureHook
def structure_defaultdict_factory(type: type[defaultdict]) -> StructureHook:
value_type = get_args(type)[1]
return make_mapping_structure_fn(type, c, partial(defaultdict, value_type))
And then you tie to all together:
c = TomlkitConverter()
c.register_structure_hook_factory(is_defaultdict, structure_defaultdict_factory)
Now you can structure defaultdicts. Since defaultdicts take an arbitrary lambda when created it won't work for all defaultdicts, but should for most.
I'll look into adding this to the next version.
from cattrs.
Thank you
Hi,
that's an interesting question. I think nobody's asked for it over the years (or I've forgotten about it); we should probably support it since it's a standard library thing.
First you need a predicate function to see if a type is a defaultdict. Note that on 3.9+ (you're on 3.11) you don't need to use
typing.DefaultDict
, you can just usecollections.defaultdict
in type hints.
Hey, thx, i didn't know it.
The problem
Something like this:
from collections import defaultdict from typing import DefaultDict, get_origin def is_defaultdict(type: Any) -> bool: """Is this type a defaultdict?""" return is_subclass(get_origin(type), (defaultdict, DefaultDict))Now you need a structure hook factory. We can wrap an existing hook factory,
cattrs.gen.make_mapping_structure_fn
. You'll need thec
Converter to be in scope.from functools import partial from typing import get_args from cattrs.gen import make_mapping_structure_fn from cattrs.dispatch import StructureHook def structure_defaultdict_factory(type: type[defaultdict]) -> StructureHook: value_type = get_args(type)[1] return make_mapping_structure_fn(type, c, partial(defaultdict, value_type))And then you tie to all together:
c = TomlkitConverter() c.register_structure_hook_factory(is_defaultdict, structure_defaultdict_factory)Now you can structure defaultdicts. Since defaultdicts take an arbitrary lambda when created it won't work for all defaultdicts, but should for most.
In short, it works, thx for the working solution, but here what i'm thinking about it:
-
it is very dynamic solution that covers all the defaultdict cases recursively yet is very implicit
-
to bring the implemention into code i have to import many additional dependecies
-
many of the dependencies take time to comprehend their responsibility
-
after that, the factory magic is not intuitive and takes even more time to comprehend 🙂
-
the question i ask myself is how the attrs field's attributes are reused? Are they ignored or partially applicaple or whatever?
-
i ended up with an explicit solution without the dynamic factory, but it doesnt work if i write type hintting. It means that i have to drop cattrs or rely on the dynamic factory ONLY removing the simple, explicit code. Here is code that doesn't work
def to_defaultdict(default_factory, data): obj = defaultdict(default_factory) for name, properties in data.items(): obj[name] = default_factory(**properties) return obj @attrs.define class LineStringProperties: pinned: bool = False comment: str = '' theme_group: str = LIGHT_THEME @attrs.define class SelectorConfig: properties: defaultdict[str, LineStringProperties] = attrs.field(converter=partial(to_defaultdict, LineStringProperties), default=to_defaultdict(LineStringProperties, {})) @staticmethod def load(config_path: Path) -> 'SelectorConfig': if not config_path.exists(): return toml_converter.structure({}, SelectorConfig) text = config_path.read_text() config = toml_converter.loads(text, SelectorConfig) return config
The idea here is simply to add an attrs converter.
The toml sample for better understanding:
[properties] [properties."alabaster_dark.toml"] pinned = true comment = "" theme_group = "light" [properties."alabaster.toml"] pinned = true comment = "" theme_group = "light"
it's getting broken on creating
obj[name] = default_factory(**properties)
insidedef to_defaultdict(default_factory, data)
raising the error while callingconfig = toml_converter.loads(text, SelectorConfig)
:| obj[name] = default_factory(**properties) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | TypeError: __main__.LineStringProperties() argument after ** must be a mapping, not LineStringProperties +------------------------------------
Imcompatibility is having type hinting and an attrs converter at the same time while structuring with
toml_converter.structure()
.
The quesiton
I'll look into adding this to the next version.
You mean the defaultdict case will be covered without the hook factory magic?
from cattrs.
You mean the defaultdict case will be covered without the hook factory magic?
Yeah, simple cases we can handle automatically. I'm working on a PR already.
I can look at your other comments later, really busy with work ATM.
No worries
from cattrs.
Related Issues (20)
- Constructor selection for structuring HOT 7
- Tagged Union: How to do structure/unstructure hook(s) for particular member type? HOT 4
- A dictionary gets structured into a list without errors/warnings HOT 2
- Question: Correct use of converters HOT 3
- Documentation does not explain unstruct_collection_overrides' input HOT 4
- Detailed validation exception groups with hook errors HOT 2
- cattrs effectively disables attrs validation HOT 3
- How to recursively unstructure with hooks? HOT 5
- Dataclass structuring fails with misleading error message.
- Default field conversion conflicts with user-defined field converter function HOT 3
- [macOS] Error when trying to run tests: `ImportError: cannot import name 'CodecOptions' from 'bson'` HOT 2
- How to hook into structuring of a simple dict? HOT 6
- Incorrect dispatch in Python 3.12 HOT 4
- Why does structuring of datetime in attrs class need a structure hook? HOT 1
- `prefer_attrib_converters` doesn't work when the field is aliased HOT 2
- Register structure hook only for optional types HOT 2
- TYPE_CHECKING and init=False HOT 5
- Problem with tagged union example HOT 3
- Subclass disambiguation for nested structures. HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from cattrs.