Skip to content
This repository has been archived by the owner on Aug 5, 2023. It is now read-only.

Dynamically Add Extra Tags to User Object #27

Open
iwoloschin opened this issue Sep 30, 2019 · 2 comments
Open

Dynamically Add Extra Tags to User Object #27

iwoloschin opened this issue Sep 30, 2019 · 2 comments

Comments

@iwoloschin
Copy link
Contributor

Is it possible to add extra tags to a user object directly from the the client's .write() method? I'd like to be able to add extra tags when calling client.write(data, **extra_tags), but this appears to only be possible with a dictionary, not a user defined class.

@iwoloschin
Copy link
Contributor Author

It appears I can potentially just "redecorate" if needed, after checking if I need extra tags, but before using my custom object. This should work for me, but going to leave this open as this seems like potentially a terrible idea that could easily break :).

@gusutabopb
Copy link
Owner

Yeah, it's not really possible when writing user-defined classes (or raw lineprotocol).
It's only possible when writing dataframes or mappings (dictionaries). For dataframes it's possible because effectively the equivalent of the to_lineprotocol method of user-defined classes is generated on the fly for every write. As for mappings it works because the serialization uses a totally different (more naive, slower) approach than the one for user-defined classes.

See this for more details:

def serialize(data, measurement=None, tag_columns=None, **extra_tags):

I think the best thing I could do is update .write docstring and/or raise a warning about it. Currently this is not documented AND ignored silently which is not a very nice API 😅

For future reference, here's a workaround you could try:

Assuming you have a user-defined class like below:

from aioinflux import *
from typing import NamedTuple
import time

@lineprotocol
class Foo(NamedTuple):
    time: TIMEINT
    x: INT
    y: INT
        
print(Foo(time.time_ns(), 1, 2).to_lineprotocol())
# b'Foo x=1i,y=2i 1591019472699583000'

You can "redecorate" your class by getting the arguments used in the first decoration from Foo.to_lineprotocol.opts (this is actually not documented, but perhaps should), modifying it to whatever you want and then using the decorator again (but as a regular function):

from copy import deepcopy
opts = deepcopy(Foo.to_lineprotocol.opts)  # deep copy to avoid modyfing the original `extra_tags` dict)
opts['extra_tags'].update({'foo': 'boo'})
print(lineprotocol(Foo, **opts)(1, 2, 3).to_lineprotocol())
b'Foo,foo=boo x=2i,y=3i 1591020382248841000'

Alternatively, in case you know then tag field key beforehand, you could make that a None value by default and use rm_none=True when decorating the function (it makes to_lineprotocol a bit slower though).

@lineprotocol(rm_none=True)
class Foo(NamedTuple):
    time: TIMEINT
    x: INT
    mynullabletag: TAG = None
        
print(Foo(time.time_ns(), 1).to_lineprotocol())
# b'Foo x=1i 1591020531182752000'

print(Foo(time.time_ns(), 1, 'mytag').to_lineprotocol())
# b'Foo,mynullabletag=mytag x=1i 1591020544735611000'

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

No branches or pull requests

2 participants