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

Feedback Requested #3

Open
imaurer opened this issue Jun 11, 2017 · 15 comments
Open

Feedback Requested #3

imaurer opened this issue Jun 11, 2017 · 15 comments

Comments

@imaurer
Copy link
Contributor

imaurer commented Jun 11, 2017

If you are using this library, please post a note here and let me know if you have any luck or any questions....

@Avsecz
Copy link

Avsecz commented Jan 29, 2018

Hey! I really like the library - it's really elegant and it works! I've been using it extensively in a package (not-yet publicly available). I'll open a separate issue about the fixed Attrs version (need to support a newer version)

@GabrielDav
Copy link
Contributor

First reasonable library I came across on this 'modern' language. Thanks for doing this. Documentation is a bit lacking and an example with JSON would be nice

@ambivalentno
Copy link
Contributor

ambivalentno commented May 22, 2018

Looks like a great library, but, I've hit unserializable decimals and enums.
Hope, I'll write some fields in a pair of hours.

@imaurer
Copy link
Contributor Author

imaurer commented May 22, 2018

Thanks @ambivalentno!

Just as an FYI, you can use Enums as ChildFields. But making it an explicit might be helpful.

Nothing with decimals explicitly or implicitly. So great to have them added.

There are a couple of tests with enums:
https://github.com/genomoncology/related/search?q=Enum

Specifically:
https://github.com/genomoncology/related/search?q=DayType

@ambivalentno
Copy link
Contributor

@imaurer Thank you for your clarification about enums. Created #13 for decimals.

@ambivalentno
Copy link
Contributor

Also, have you thought about serialization/deserialization speed comparison with existing libraries?

@imaurer
Copy link
Contributor Author

imaurer commented May 22, 2018

Merged the PR. Thanks again. Will update the version shortly.

Which respect to speed comparisons? Do you mean with using ujson instead or json for converting to JSON or comparing related to something like cattrs or schematics?

I'm interested, but not exactly a top priority since I mostly use this library for configuration files and other non-performance critical features. But moar speed is always great.

@ambivalentno
Copy link
Contributor

Do you mean with using ujson instead or json for converting to JSON or comparing related to something like cattrs or schematics?
smth like cattrs/marshmallow/serpy/etc.
Thank you for clarification.

@olfway
Copy link

olfway commented Jun 29, 2018

Hello! I'm playing with related and it looks really nice!

Could you help with this case:

conf:
  A:
    type: a
    field_a: 1
  B:
    type: b
    filed_b: 2

At the end, I want to have a list with two different classes:

[
    ClassA(name='A', type='a', field_a=1),
    ClassB(name='B', type='b', field_b=2)
]

is it possible?

@imaurer imaurer closed this as completed Jun 29, 2018
@imaurer imaurer reopened this Jun 29, 2018
@imaurer
Copy link
Contributor Author

imaurer commented Jun 29, 2018

Hard to tell from an artificial example your exact goals, but both of these examples work. I prefer the 2nd example but it doesn't meet your specific requirement of 2 different child class types. It also uses two special flags in the YAML serializer function.

Example 1

import related


@related.mutable()
class BaseChild(object):
    type = related.StringField()


@related.mutable()
class ChildA(BaseChild):
    field_a = related.IntegerField()


@related.mutable()
class ChildB(BaseChild):
    field_b = related.IntegerField()


@related.mutable()
class Parent(object):
    A = related.ChildField(ChildA)
    B = related.ChildField(ChildB)


@related.mutable()
class Root(object):
    conf = related.ChildField(Parent)


example = """
conf:
  A:
    type: a
    field_a: 1
  B:
    type: b
    field_b: 2
""".strip()

roundtrip = related.to_yaml(related.from_yaml(example, Root)).strip()
print(roundtrip)
assert roundtrip == example

Example 2

@related.mutable()
class ChildAB(object):
    name = related.StringField()
    type = related.StringField()
    field_a = related.IntegerField(required=False)
    field_b = related.IntegerField(required=False)


@related.mutable()
class RootAB(object):
    conf = related.MappingField(ChildAB, "name")


example = """
conf:
  A:
    type: a
    field_a: 1
  B:
    type: b
    field_b: 2
""".strip()

root_ab = related.from_yaml(example, RootAB)
roundtrip = related.to_yaml(
    root_ab, suppress_map_key_values=True, suppress_empty_values=True
).strip()
print(roundtrip)
assert roundtrip == example

Hope that helps!
Ian

@olfway
Copy link

olfway commented Jun 29, 2018

Thanks for your answer, Ian!

I want different classes because I want to validate that:

  • ChildA always has "field_a" and no "field_b"
  • ChildB always has "field_b" and no "field_a"

The first example validates it as I want, but unfortunately, I cannot tell how many entries will be in the conf map.

It's a config file and there can be several entries, some with type=a and others with type=b
Actually, It's a list of actions, like

actions:
  run-prog-a:
    type: program
    command: /some/program
  run-prog-b
    type: program
    command: /some/other/program
  make-http-call:
    type: http
    url: http://some.site/

So, I want to make sure, that run-prog-* actions will always have "command" field
and make-http-* actions will have "url" field instead

@imaurer
Copy link
Contributor Author

imaurer commented Jun 29, 2018

@olfway since this is so specialized you might want to create your own field type with it's own converter that invokes to_model passing in a class based on a child dictionary value. It's not super hard to do once you understand the pattern.

Here is the "field type" to copy:

def MappingField(cls, child_key, default=NOTHING, required=True, repr=False,

Here is the converter to copy:

def to_mapping_field(cls, key): # pragma: no mccabe

I provided the links to the Mapping Field, but since order probably is important, you might want to consider switching to the Sequence Field as your starting point.

@olfway
Copy link

olfway commented Jun 29, 2018

Thanks, I'll try it

@bergerr
Copy link

bergerr commented Feb 8, 2019

I like this library a lot, but I can't get past a problem I'm having with SeqeunceFields.

I have an Authorization object:

@related.mutable
class Authorization():
    id = related.StringField(required=False)
    description = related.StringField(required=False)
    capabilities = related.SequenceField(Capability, required=False)

    @staticmethod
    def load(dictionary):
        return to_model(Authorization, dictionary)

    def provide(self):
        d = to_dict(self)
        return d

But whenever I try to pass a dict with a lift of Capability objects into the load function it gives me an error.

for val in result:
    capability_vals = (val[2], val[3])
    capability_zip = dict(zip(capability_keys, capability_vals))
    capability = Capability.load(capability_zip)
    capability_list.append(capability)

    auth_vals = (val[0], val[1], capability_list)
    auth_zip = dict(zip(auth_keys, auth_vals))
    auth = Authorization.load(auth_zip)

This gives me: WARNING routes : isinstance() arg 2 must be a type or tuple of types

I've tried converting passing in tuple(capabilities_list), a list, a tuple of numbers, a tuple of strings, etc. The only thing that seems to work is passing in capabilities directly as an empty tuple:
auth = Authorization.load({"id": "1", "description": "1", "capabilities": ()})

What am I doing wrong?


Edit: I figured it out, I was importing the Authorization class wrong.

@AndydeCleyre
Copy link

AndydeCleyre commented Apr 28, 2019

A lot about this library is really appealing, thank you. I've tried it out by starting a DYN implementation with it.

Some of the questions I had while doing it:

  1. Are my reform_as and equal_as functions redundant or foolish?
  2. Should I not be using inheritance so much?
  3. Why does related.to_yaml put single quotes around some enum values but not others?
  4. Am I using (im)mutable decorators rationally?
  5. Can I somehow pass just a sequence/collection of related objects to the constructor of one which has a MappingField of those? It seems redundant to construct a dictionary, when the MappingField itself has the info needed to construct that (child_key).
  6. Any unaddressed glaring abuses of the library?

EDIT: I realize that Q3 is probably because of (broad-)yaml's implicit boolean conversions. Is it possible to disable those, so that yes and no are interpreted as strings and written without quotes?

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

No branches or pull requests

7 participants