Search code examples
pythontuplesunpack

Best Practice to Unpack Longer Tuples (Python 3.6)


Passing a longer tuple into a function, and want to unpack into specific variables.

High-level:

  • I instrument some metrics (Prometheus), let's say 5-6 of them
  • Metrics get put into a tuple, and passed into a function as an arg
  • Unpacking in said function seems to result in some of the new variables actually being type 'tuple' rather than the Prometheus Metric Objects I'm expecting

Given what I'm trying to do, my question are:

  • Am I properly using / unpacking the tuple?
  • Is there possibly a limitation in tuple use that explicitly impacts this?
  • Is there a better approach altogether in how to manage this scenario? I went with a tuple because I like the immutability, and the ability to explicitly assign variables to the particular metric objects. But if there's a better way, I'm entirely open to it.

Already Tried:

  • Confirming the number of vars matches the number of metrics in the tuple

Here's the instrumentation and placement into tuple:

multicast_sequence_problem_total = Counter(
    "multicast_sequence_problem_total",
    "Total of Sequence Errors (Multicast)",
    ['source_dc', 'destination_dc', 'vlan']
),
unicast_sequence_problem_total = Counter(
    "unicast_sequence_problem_total",
    "Total of Sequence Errors (Unicast)",
    ['source_dc', 'destination_dc', 'vlan']
),
multicast_duplicate_total = Counter(
    "multicast_duplicate_total",
    "Total of Duplicate Sequences (Multicast)",
    ['source_dc', 'destination_dc', 'vlan']
),
unicast_duplicate_total = Counter(
    "unicast_duplicate_total",
    "Total of Duplicate Sequences (Unicast)",
    ['source_dc', 'destination_dc', 'vlan']
)
latency_gauge = Gauge(
    "latency_average_gauge",
    "Latency Average",
    ['protocol', 'source_dc', 'destination_dc', 'vlan']
)
metrics = (
    multicast_sequence_problem_total,
    multicast_duplicate_total,
    unicast_sequence_problem_total,
    unicast_duplicate_total,
    latency_gauge
)

Skip to my other function where I actually increment / set the metric values -- here's the unpack:

mcast_seq, mcast_dup, uni_seq, uni_dup, avg_latency = metrics_tuple

Went in with pdb in order to debug, and found that 3 of the 5 metrics are actually type: tuple:

(Pdb) type(metrics_tuple)
<class 'tuple'>
(Pdb) type(avg_latency)
<class 'prometheus_client.metrics.Gauge'>
(Pdb) type(mcast_seq)
<class 'tuple'>
(Pdb) type(mcast_dup)
<class 'tuple'>
(Pdb) type(uni_seq)
<class 'tuple'>
(Pdb) type(uni_dup)
<class 'prometheus_client.metrics.Counter'>
(Pdb)     

So this is causing some errors when I get farther along in my code. Python thinks I'm trying to unpack in other places (where I'm not expecting a tuple!)


Solution

  • Discovered the answer after using a dictionary rather than tuple. My metrics objects were tuples, and the reason is that I forgot to remove some commas between the metrics where they're instrumented.

    That only would have made sense if I instrumented while instantiating the dictionary or tuple all at the same time (which I didn't).

    (facepalm) -- Took a while to figure out, but the pdb output saved me. Showed me that only some of the metrics were tuples, not all of them. Seeking out the difference between them led me to the answer.