Skip to content

Commit 6ebe610

Browse files
authored
cleanups, fixes & docs in Bus.__new__() and util.load_config() (hardbyte#309)
1 parent 4ba482f commit 6ebe610

2 files changed

Lines changed: 64 additions & 41 deletions

File tree

can/interface.py

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
CyclicSendTasks.
88
"""
99

10-
from __future__ import absolute_import
10+
from __future__ import absolute_import, print_function
1111

1212
import sys
1313
import importlib
@@ -98,36 +98,37 @@ class Bus(BusABC):
9898
configuration file from default locations.
9999
"""
100100

101-
@classmethod
102-
def __new__(cls, other, channel=None, *args, **kwargs):
101+
@staticmethod
102+
def __new__(cls, *args, **config):
103103
"""
104-
Takes the same arguments as :class:`can.BusABC` with the addition of:
104+
Takes the same arguments as :class:`can.BusABC.__init__` with the addition of:
105105
106-
:param kwargs:
107-
Should contain a bustype key with a valid interface name.
106+
:param dict config:
107+
Should contain an ``interface`` key with a valid interface name. If not,
108+
it is completed using :meth:`can.util.load_config`.
108109
109-
:raises:
110-
NotImplementedError if the bustype isn't recognized
111-
:raises:
112-
ValueError if the bustype or channel isn't either passed as an argument
113-
or set in the can.rc config.
110+
:raises: NotImplementedError
111+
if the ``interface`` isn't recognized
114112
113+
:raises: ValueError
114+
if the ``channel`` could not be determined
115115
"""
116116

117-
# Figure out the configuration
118-
config = load_config(config={
119-
'interface': kwargs.get('bustype', kwargs.get('interface')),
120-
'channel': channel
121-
})
122-
123-
# remove the bustype & interface so it doesn't get passed to the backend
124-
if 'bustype' in kwargs:
125-
del kwargs['bustype']
126-
if 'interface' in kwargs:
127-
del kwargs['interface']
117+
# figure out the rest of the configuration; this might raise an error
118+
config = load_config(config=config)
128119

120+
# resolve the bus class to use for that interface
129121
cls = _get_class_for_interface(config['interface'])
130-
return cls(channel=config['channel'], *args, **kwargs)
122+
123+
# remove the 'interface' key so it doesn't get passed to the backend
124+
del config['interface']
125+
126+
# make sure the bus can handle this config
127+
if 'channel' not in config:
128+
raise ValueError("channel argument missing")
129+
130+
# the channel attribute should be present in **config
131+
return cls(*args, **config)
131132

132133

133134
def detect_available_configs(interfaces=None):

can/util.py

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -121,57 +121,79 @@ def load_config(path=None, config=None):
121121
If you pass ``"socketcan"`` this automatically selects between the
122122
native and ctypes version.
123123
124+
.. note::
125+
126+
The key ``bustype`` is copied to ``interface`` if that one is missing
127+
and does never appear in the result.
128+
124129
:param path:
125130
Optional path to config file.
131+
126132
:param config:
127133
A dict which may set the 'interface', and/or the 'channel', or neither.
134+
It may set other values that are passed through.
128135
129136
:return:
130137
A config dictionary that should contain 'interface' & 'channel'::
131138
132139
{
133140
'interface': 'python-can backend interface to use',
134141
'channel': 'default channel to use',
142+
# possibly more
135143
}
136144
137145
Note ``None`` will be used if all the options are exhausted without
138146
finding a value.
147+
148+
All unused values are passed from ``config`` over to this.
149+
150+
:raises:
151+
NotImplementedError if the ``interface`` isn't recognized
139152
"""
140-
if config is None:
141-
config = {}
142153

143-
system_config = {}
144-
configs = [
145-
config,
154+
# start with an empty dict to apply filtering to all sources
155+
given_config = config
156+
config = {}
157+
158+
# use the given dict for default values
159+
config_sources = [
160+
given_config,
146161
can.rc,
147162
load_environment_config,
148163
lambda: load_file_config(path)
149164
]
150165

151166
# Slightly complex here to only search for the file config if required
152-
for cfg in configs:
167+
for cfg in config_sources:
153168
if callable(cfg):
154169
cfg = cfg()
170+
# remove legacy operator (and copy to interface if not already present)
171+
if 'bustype' in cfg:
172+
if 'interface' not in cfg or not cfg['interface']:
173+
cfg['interface'] = cfg['bustype']
174+
del cfg['bustype']
175+
# copy all new parameters
155176
for key in cfg:
156-
if key not in system_config and cfg[key] is not None:
157-
system_config[key] = cfg[key]
177+
if key not in config:
178+
config[key] = cfg[key]
158179

159180
# substitute None for all values not found
160181
for key in REQUIRED_KEYS:
161-
if key not in system_config:
162-
system_config[key] = None
182+
if key not in config:
183+
config[key] = None
163184

164-
if system_config['interface'] == 'socketcan':
165-
system_config['interface'] = choose_socketcan_implementation()
185+
# this is done later too but better safe than sorry
186+
if config['interface'] == 'socketcan':
187+
config['interface'] = choose_socketcan_implementation()
166188

167-
if system_config['interface'] not in VALID_INTERFACES:
168-
raise NotImplementedError('Invalid CAN Bus Type - {}'.format(system_config['interface']))
189+
if config['interface'] not in VALID_INTERFACES:
190+
raise NotImplementedError('Invalid CAN Bus Type - {}'.format(config['interface']))
169191

170-
if 'bitrate' in system_config:
171-
system_config['bitrate'] = int(system_config['bitrate'])
192+
if 'bitrate' in config:
193+
config['bitrate'] = int(config['bitrate'])
172194

173-
can.log.debug("can config: {}".format(system_config))
174-
return system_config
195+
can.log.debug("loaded can config: {}".format(config))
196+
return config
175197

176198

177199
def choose_socketcan_implementation():

0 commit comments

Comments
 (0)