aioinflux
aioinflux copied to clipboard
Dynamically Add Extra Tags to User Object
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.
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 :).
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: https://github.com/gusutabopb/aioinflux/blob/0bc49c497d2cacb3381d12ab0ff20f15c8e89d8a/aioinflux/serialization/init.py#L9
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'