Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cattrs does not unstructure os.PathLike to str #417

Open
intelfx opened this issue Aug 17, 2023 · 3 comments
Open

cattrs does not unstructure os.PathLike to str #417

intelfx opened this issue Aug 17, 2023 · 3 comments

Comments

@intelfx
Copy link

intelfx commented Aug 17, 2023

  • cattrs version: 23.1.2
  • Python version: 3.11.3
  • Operating System: Arch Linux

Description

When unstructuring an attrs class where one of the fields is annotated as os.PathLike (and contains a Path), that field remains a Path (in my case, PosixPath) instead of being unstructured as str.

Curiously, annotating the field as Union[str, os.PathLike] or pathlib.Path (without changing the actual data type) resolves the issue.

What I Did

In [1]: import os
   ...: from pathlib import Path
   ...: from typing import Union
   ...: 
   ...: import attrs
   ...: import cattrs
   ...: import cattrs.preconf.json

In [2]: @attrs.define
   ...: class MyClass:
   ...:     foo: Union[str, os.PathLike] = Path("/foo")
   ...:     bar: os.PathLike = Path("/bar")
   ...: 

In [3]: cattrs.preconf.json.make_converter().unstructure(MyClass())
Out[3]: {'foo': '/foo', 'bar': PosixPath('/bar')}

In [4]: cattrs.unstructure(MyClass())
Out[4]: {'foo': '/foo', 'bar': PosixPath('/bar')}

I'm not saying that this must be a bug (rather than merely my mis-usage of cattrs), but I was not able to find anything in the docs that would suggest that I was doing something incorrectly.

@Tinche
Copy link
Member

Tinche commented Aug 18, 2023

We don't support os.PathLike, no one has asked for it yet. pathlib.Path is supported though.

When generating the unstructuring code, cattrs looks at the type of the field in the class rather than the actual runtime value of the field. If cattrs doesn't know how to unstructure a field, it just passes it through, which is what you're seeing. (For structuring it's different, there it would be an exception.)

To solve the issue, you can register a super simple hook yourself:

c.register_unstructure_hook(PathLike, str)

@intelfx
Copy link
Author

intelfx commented Aug 18, 2023

If cattrs doesn't know how to unstructure a field, it just passes it through, which is what you're seeing.

I see. This was not obvious from the documentation.

Perhaps there is value in a "strict mode" that would ensure that either everything is unstructured to primitive types or an exception is thrown? Such a mode could then be auto-enabled for the preconfigured converters.

@Tinche
Copy link
Member

Tinche commented Aug 21, 2023

I can consider it, but what's the use case? Either cattrs raises something or your json lib raises something, feels like it ends up being the same?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants