Skip to content

Commit 067cddd

Browse files
committed
Refactor to avoid the use of a special global object.
The global object formerly used is now replaced by direct use of the namespace opf the globals module. This eliminates the redundant getters and setters and simplifies the code for future maintainers. Note that the globals module name conflicts (harmlessly at present) with a Python built-in function. A future commit should rename it `config` to remove this clash and better represent its intended purpose.
1 parent b280d0b commit 067cddd

8 files changed

Lines changed: 168 additions & 273 deletions

File tree

meshtastic/__main__.py

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,14 @@
1616

1717
import meshtastic.test
1818
import meshtastic.util
19+
from meshtastic import globals
1920
from meshtastic import channel_pb2, config_pb2, portnums_pb2, remote_hardware, BROADCAST_ADDR
2021
from meshtastic.version import get_active_version
2122
from meshtastic.ble_interface import BLEInterface
22-
from meshtastic.globals import Globals
23-
2423

2524
def onReceive(packet, interface):
2625
"""Callback invoked when a packet arrives"""
27-
our_globals = Globals.getInstance()
28-
args = our_globals.get_args()
26+
args = globals.args
2927
try:
3028
d = packet.get("decoded")
3129
logging.debug(f"in onReceive() d:{d}")
@@ -69,7 +67,7 @@ def getPref(node, comp_name):
6967
# Note: protobufs has the keys in snake_case, so snake internally
7068
snake_name = meshtastic.util.camel_to_snake(name[1])
7169
logging.debug(f"snake_name:{snake_name} camel_name:{camel_name}")
72-
logging.debug(f"use camel:{Globals.getInstance().get_camel_case()}")
70+
logging.debug(f"use camel:{globals.camel_case}")
7371

7472
# First validate the input
7573
localConfig = node.localConfig
@@ -86,7 +84,7 @@ def getPref(node, comp_name):
8684
break
8785

8886
if not found:
89-
if Globals.getInstance().get_camel_case():
87+
if globals.camel_case:
9088
print(
9189
f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have an attribute {snake_name}."
9290
)
@@ -105,7 +103,7 @@ def getPref(node, comp_name):
105103
config_values = getattr(config, config_type.name)
106104
if not wholeField:
107105
pref_value = getattr(config_values, pref.name)
108-
if Globals.getInstance().get_camel_case():
106+
if globals.camel_case:
109107
print(f"{str(config_type.name)}.{camel_name}: {str(pref_value)}")
110108
logging.debug(
111109
f"{str(config_type.name)}.{camel_name}: {str(pref_value)}"
@@ -171,7 +169,7 @@ def setPref(config, comp_name, valStr) -> bool:
171169
if e:
172170
val = e.number
173171
else:
174-
if Globals.getInstance().get_camel_case():
172+
if globals.camel_case:
175173
print(
176174
f"{name[0]}.{camel_name} does not have an enum called {val}, so you can not set it."
177175
)
@@ -210,7 +208,7 @@ def setPref(config, comp_name, valStr) -> bool:
210208
config_type.message_type.ignore_incoming.extend([val])
211209

212210
prefix = f"{name[0]}." if config_type.message_type is not None else ""
213-
if Globals.getInstance().get_camel_case():
211+
if globals.camel_case:
214212
print(f"Set {prefix}{camel_name} to {valStr}")
215213
else:
216214
print(f"Set {prefix}{snake_name} to {valStr}")
@@ -225,8 +223,7 @@ def onConnected(interface):
225223
False # Should we wait for an acknowledgment if we send to a remote node?
226224
)
227225
try:
228-
our_globals = Globals.getInstance()
229-
args = our_globals.get_args()
226+
args = globals.args
230227

231228
# do not print this line if we are exporting the config
232229
if not args.export_config:
@@ -477,7 +474,7 @@ def onConnected(interface):
477474
print("Writing modified preferences to device")
478475
node.writeConfig(field)
479476
else:
480-
if Globals.getInstance().get_camel_case():
477+
if globals.camel_case:
481478
print(
482479
f"{node.localConfig.__class__.__name__} and {node.moduleConfig.__class__.__name__} do not have an attribute {pref[0]}."
483480
)
@@ -590,7 +587,7 @@ def onConnected(interface):
590587
# handle changing channels
591588

592589
if args.ch_add:
593-
channelIndex = our_globals.get_channel_index()
590+
channelIndex = globals.channel_index
594591
if channelIndex is not None:
595592
# Since we set the channel index after adding a channel, don't allow --ch-index
596593
meshtastic.util.our_exit(
@@ -621,12 +618,12 @@ def onConnected(interface):
621618
n.writeChannel(ch.index)
622619
if channelIndex is None:
623620
print(f"Setting newly-added channel's {ch.index} as '--ch-index' for further modifications")
624-
our_globals.set_channel_index(ch.index)
621+
globals.channel_index = ch.index
625622

626623
if args.ch_del:
627624
closeNow = True
628625

629-
channelIndex = our_globals.get_channel_index()
626+
channelIndex = globals.channel_index
630627
if channelIndex is None:
631628
meshtastic.util.our_exit(
632629
"Warning: Need to specify '--ch-index' for '--ch-del'.", 1
@@ -642,7 +639,7 @@ def onConnected(interface):
642639

643640
def setSimpleConfig(modem_preset):
644641
"""Set one of the simple modem_config"""
645-
channelIndex = our_globals.get_channel_index()
642+
channelIndex = globals.channel_index
646643
if channelIndex is not None and channelIndex > 0:
647644
meshtastic.util.our_exit(
648645
"Warning: Cannot set modem preset for non-primary channel", 1
@@ -677,7 +674,7 @@ def setSimpleConfig(modem_preset):
677674
if args.ch_set or args.ch_enable or args.ch_disable:
678675
closeNow = True
679676

680-
channelIndex = our_globals.get_channel_index()
677+
channelIndex = globals.channel_index
681678
if channelIndex is None:
682679
meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1)
683680
ch = interface.getNode(args.dest).channels[channelIndex]
@@ -832,7 +829,7 @@ def printConfig(config):
832829
names = []
833830
for field in config.message_type.fields:
834831
tmp_name = f"{config_section.name}.{field.name}"
835-
if Globals.getInstance().get_camel_case():
832+
if globals.camel_case:
836833
tmp_name = meshtastic.util.snake_to_camel(tmp_name)
837834
names.append(tmp_name)
838835
for temp_name in sorted(names):
@@ -877,7 +874,7 @@ def export_config(interface):
877874
if owner_short:
878875
configObj["owner_short"] = owner_short
879876
if channel_url:
880-
if Globals.getInstance().get_camel_case():
877+
if globals.camel_case:
881878
configObj["channelUrl"] = channel_url
882879
else:
883880
configObj["channel_url"] = channel_url
@@ -889,11 +886,11 @@ def export_config(interface):
889886
# Convert inner keys to correct snake/camelCase
890887
prefs = {}
891888
for pref in config:
892-
if Globals.getInstance().get_camel_case():
889+
if globals.camel_case:
893890
prefs[meshtastic.util.snake_to_camel(pref)] = config[pref]
894891
else:
895892
prefs[pref] = config[pref]
896-
if Globals.getInstance().get_camel_case():
893+
if globals.camel_case:
897894
configObj["config"] = config
898895
else:
899896
configObj["config"] = config
@@ -905,7 +902,7 @@ def export_config(interface):
905902
for pref in module_config:
906903
if len(module_config[pref]) > 0:
907904
prefs[pref] = module_config[pref]
908-
if Globals.getInstance().get_camel_case():
905+
if globals.camel_case:
909906
configObj["module_config"] = prefs
910907
else:
911908
configObj["module_config"] = prefs
@@ -919,9 +916,8 @@ def export_config(interface):
919916
def common():
920917
"""Shared code for all of our command line wrappers"""
921918
logfile = None
922-
our_globals = Globals.getInstance()
923-
args = our_globals.get_args()
924-
parser = our_globals.get_parser()
919+
args = globals.args
920+
parser = globals.parser
925921
logging.basicConfig(
926922
level=logging.DEBUG if (args.debug or args.listen) else logging.INFO,
927923
format="%(levelname)s file:%(filename)s %(funcName)s line:%(lineno)s %(message)s",
@@ -937,7 +933,7 @@ def common():
937933

938934
if args.ch_index is not None:
939935
channelIndex = int(args.ch_index)
940-
our_globals.set_channel_index(channelIndex)
936+
globals.channel_index = channelIndex
941937

942938
if not args.dest:
943939
args.dest = BROADCAST_ADDR
@@ -972,7 +968,7 @@ def common():
972968
# Note: using "line buffering"
973969
# pylint: disable=R1732
974970
logfile = open(args.seriallog, "w+", buffering=1, encoding="utf8")
975-
our_globals.set_logfile(logfile)
971+
globals.logfile = logfile
976972

977973
subscribe()
978974
if args.ble_scan:
@@ -1063,9 +1059,8 @@ def addConnectionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParse
10631059

10641060
def initParser():
10651061
"""Initialize the command line argument parsing."""
1066-
our_globals = Globals.getInstance()
1067-
parser = our_globals.get_parser()
1068-
args = our_globals.get_args()
1062+
parser = globals.parser
1063+
args = globals.args
10691064

10701065
# The "Help" group includes the help option and other informational stuff about the CLI itself
10711066
outerHelpGroup = parser.add_argument_group('Help')
@@ -1286,7 +1281,7 @@ def initParser():
12861281
)
12871282

12881283
group.add_argument(
1289-
"--request-telemetry",
1284+
"--request-telemetry",
12901285
help="Request telemetry from a node. "
12911286
"You need pass the destination ID as argument with '--dest'. "
12921287
"For repeaters, the nodeNum is required.",
@@ -1431,34 +1426,32 @@ def initParser():
14311426

14321427

14331428
args = parser.parse_args()
1434-
our_globals.set_args(args)
1435-
our_globals.set_parser(parser)
1429+
globals.args = args
1430+
globals.parser = parser
14361431

14371432

14381433
def main():
14391434
"""Perform command line meshtastic operations"""
1440-
our_globals = Globals.getInstance()
14411435
parser = argparse.ArgumentParser(
14421436
add_help=False,
14431437
epilog="If no connection arguments are specified, we search for a compatible serial device, "
14441438
"and if none is found, then attempt a TCP connection to localhost.")
1445-
our_globals.set_parser(parser)
1439+
globals.parser = parser
14461440
initParser()
14471441
common()
1448-
logfile = our_globals.get_logfile()
1442+
logfile = globals.logfile
14491443
if logfile:
14501444
logfile.close()
14511445

14521446

14531447
def tunnelMain():
14541448
"""Run a meshtastic IP tunnel"""
1455-
our_globals = Globals.getInstance()
14561449
parser = argparse.ArgumentParser(add_help=False)
1457-
our_globals.set_parser(parser)
1450+
globals.parser = parser
14581451
initParser()
1459-
args = our_globals.get_args()
1452+
args = globals.args
14601453
args.tunnel = True
1461-
our_globals.set_args(args)
1454+
globals.args = args
14621455
common()
14631456

14641457

meshtastic/globals.py

Lines changed: 23 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,28 @@
1-
"""Globals singleton class.
2-
3-
Instead of using a global, stuff your variables in this "trash can".
4-
This is not much better than using python's globals, but it allows
5-
us to better test meshtastic. Plus, there are some weird python
6-
global issues/gotcha that we can hopefully avoid by using this
7-
class instead.
8-
91
"""
2+
Globals singleton class.
103
4+
The Global object is gone, as are all its setters and getters. Instead the
5+
module itself is the singleton namespace, which can be imported into
6+
whichever module is used. The associated tests have also been removed,
7+
since we now rely on built in Python mechanisms.
118
12-
class Globals:
13-
"""Globals class is a Singleton."""
14-
15-
__instance = None
16-
17-
@staticmethod
18-
def getInstance():
19-
"""Get an instance of the Globals class."""
20-
if Globals.__instance is None:
21-
Globals()
22-
return Globals.__instance
23-
24-
def __init__(self):
25-
"""Constructor for the Globals CLass"""
26-
if Globals.__instance is not None:
27-
raise Exception("This class is a singleton") # pylint: disable=W0719
28-
else:
29-
Globals.__instance = self
30-
self.args = None
31-
self.parser = None
32-
self.channel_index = None
33-
self.logfile = None
34-
self.tunnelInstance = None
35-
# TODO: to migrate to camel_case for v1.3 change this value to True
36-
self.camel_case = False
37-
38-
def reset(self):
39-
"""Reset all of our globals. If you add a member, add it to this method, too."""
40-
self.args = None
41-
self.parser = None
42-
self.channel_index = None
43-
self.logfile = None
44-
self.tunnelInstance = None
45-
# TODO: to migrate to camel_case for v1.3 change this value to True
46-
self.camel_case = False
47-
48-
# setters
49-
def set_args(self, args):
50-
"""Set the args"""
51-
self.args = args
52-
53-
def set_parser(self, parser):
54-
"""Set the parser"""
55-
self.parser = parser
56-
57-
def set_channel_index(self, channel_index):
58-
"""Set the channel_index"""
59-
self.channel_index = channel_index
9+
This is intended to make the Python read more naturally, and to make the
10+
intention of the code clearer and more compact. It is merely a sticking
11+
plaster over the use of shared globals, but the coupling issues wil be dealt
12+
with rather more easily once the code is simplified by this change.
6013
61-
def set_logfile(self, logfile):
62-
"""Set the logfile"""
63-
self.logfile = logfile
64-
65-
def set_tunnelInstance(self, tunnelInstance):
66-
"""Set the tunnelInstance"""
67-
self.tunnelInstance = tunnelInstance
68-
69-
def set_camel_case(self):
70-
"""Force using camelCase for things like prefs/set/set"""
71-
self.camel_case = True
72-
73-
# getters
74-
def get_args(self):
75-
"""Get args"""
76-
return self.args
77-
78-
def get_parser(self):
79-
"""Get parser"""
80-
return self.parser
81-
82-
def get_channel_index(self):
83-
"""Get channel_index"""
84-
return self.channel_index
85-
86-
def get_logfile(self):
87-
"""Get logfile"""
88-
return self.logfile
89-
90-
def get_tunnelInstance(self):
91-
"""Get tunnelInstance"""
92-
return self.tunnelInstance
14+
"""
9315

94-
def get_camel_case(self):
95-
"""Get whether or not to use camelCase"""
96-
return self.camel_case
16+
def reset():
17+
"""
18+
"""
19+
global args, parser, channel_index, logfile, tunnelInstance, camel_case
20+
args = None
21+
parser = None
22+
channel_index = None
23+
logfile = None
24+
tunnelInstance = None
25+
# TODO: to migrate to camel_case for v1.3 change this value to True
26+
camel_case = False
27+
28+
reset()

meshtastic/tests/conftest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import pytest
77

8-
from meshtastic.__main__ import Globals
8+
from meshtastic import globals
99

1010
from ..mesh_interface import MeshInterface
1111

@@ -15,8 +15,8 @@ def reset_globals():
1515
"""Fixture to reset globals."""
1616
parser = None
1717
parser = argparse.ArgumentParser(add_help=False)
18-
Globals.getInstance().reset()
19-
Globals.getInstance().set_parser(parser)
18+
globals.reset()
19+
globals.parser = parser
2020

2121

2222
@pytest.fixture

0 commit comments

Comments
 (0)