def test_file_format_error(self): """Config parsing error should be suppressed.""" with mock.patch("dwave.cloud.config.open", mock.mock_open(read_data="|\na|b,c"), create=True): self.assertEqual(legacy_load_config(key='a'), ('b', 'c', None, None)) with mock.patch("dwave.cloud.config.open", mock.mock_open(read_data="|"), create=True): self.assertRaises(ValueError, legacy_load_config)
def from_config(cls, config_file=None, profile=None, client=None, endpoint=None, token=None, solver=None, proxy=None): """Client factory method which loads configuration from file(s), process environment variables and explicitly provided values, creating and returning the appropriate client instance (:class:`dwave.cloud.qpu.Client` or :class:`dwave.cloud.sw.Client`). Usage example: Create ``dwave.conf`` in your current directory or ``~/.config/dwave/dwave.conf``:: [prod] endpoint = https://cloud.dwavesys.com/sapi token = DW-123123-secret solver = DW_2000Q_1 Run: >>> from dwave.cloud import Client >>> client = Client.from_config(profile='prod') >>> solver = client.get_solver() >>> computation = solver.submit({}, {}) >>> samples = computation.result() TODO: describe config loading, new config in broad strokes, refer to actual loaders' doc; include examples for config and usage. """ # try loading configuration from a preferred new config subsystem # (`./dwave.conf`, `~/.config/dwave/dwave.conf`, etc) try: config = load_config(config_file=config_file, profile=profile, client=client, endpoint=endpoint, token=token, solver=solver, proxy=proxy) except ValueError: config = dict(endpoint=endpoint, token=token, solver=solver, proxy=proxy, client=client) # and failback to the legacy `.dwrc` if config.get('token') is None or config.get('endpoint') is None: try: _endpoint, _token, _proxy, _solver = legacy_load_config( key=profile, endpoint=endpoint, token=token, solver=solver, proxy=proxy) config = dict(endpoint=_endpoint, token=_token, solver=_solver, proxy=_proxy, client=client) except (ValueError, IOError): pass from dwave.cloud import qpu, sw _clients = {'qpu': qpu.Client, 'sw': sw.Client} _client = config.pop('client') or 'qpu' return _clients[_client](**config)
from __future__ import absolute_import import unittest try: import unittest.mock as mock except ImportError: import mock from dwave.cloud.config import legacy_load_config from dwave.cloud.qpu import Client from dwave.cloud.exceptions import SolverAuthenticationError import dwave.cloud try: config_url, config_token, _, config_solver = legacy_load_config() if None in [config_url, config_token, config_solver]: raise ValueError() skip_live = False except: skip_live = True class ConnectivityTests(unittest.TestCase): """Test connecting and related failure modes.""" @unittest.skipIf(skip_live, "No live server available.") def test_bad_url(self): """Connect with a bad URL.""" with self.assertRaises(IOError): client = Client("not-a-url", config_token) client.get_solvers()
def from_config(cls, config_file=None, profile=None, client=None, endpoint=None, token=None, solver=None, proxy=None, legacy_config_fallback=True, **kwargs): """Client factory method which loads configuration from file(s), process environment variables and explicitly provided values, creating and returning the appropriate client instance (:class:`dwave.cloud.qpu.Client` or :class:`dwave.cloud.sw.Client`). Note: For details on config loading from files and environment, please see :func:`~dwave.cloud.config.load_config`. Args: config_file (str/None/False/True, default=None): Path to config file. ``None`` for auto-detect, ``False`` to skip loading from any file (including auto-detection), and ``True`` to force auto-detection, disregarding environment value for config file. profile (str, default=None): Profile name (config file section name). If undefined it is taken from ``DWAVE_PROFILE`` environment variable, or config file, or first section, or defaults. For details, see :func:`~dwave.cloud.config.load_config`. client (str, default=None): Client class (selected by name) to use for accessing the API. Use ``qpu`` to specify the :class:`dwave.cloud.qpu.Client` and ``sw`` for :class:`dwave.cloud.sw.Client`. endpoint (str, default=None): API endpoint URL. token (str, default=None): API authorization token. solver (str, default=None): Default solver to use in :meth:`~dwave.cloud.client.Client.get_solver`. If undefined, you'll have to explicitly specify the solver name/id in all calls to :meth:`~dwave.cloud.client.Client.get_solver`. proxy (str, default=None): URL for proxy to use in connections to D-Wave API. Can include username/password, port, scheme, etc. If undefined, client will connect directly to the API (unless you use a system-level proxy). legacy_config_fallback (bool, default=True): If loading from a ``dwave.conf`` config file fails, try loading the ``.dwrc`` legacy config. **kwargs: All remaining keyword arguments are passed-through as-is to the chosen `Client` constructor method. A notable custom argument is `permissive_ssl`. Note: all user-defined keys from config files are propagated to the `Client` constructor too, and can be overridden with these keyword arguments. Example: Create ``dwave.conf`` in your current directory or ``~/.config/dwave/dwave.conf``:: [prod] endpoint = https://cloud.dwavesys.com/sapi token = DW-123123-secret solver = DW_2000Q_1 Run:: from dwave.cloud import Client with Client.from_config(profile='prod') as client: solver = client.get_solver() computation = solver.sample_ising({}, {}) samples = computation.result() Raises: :exc:`~dwave.cloud.exceptions.ConfigFileReadError`: Config file specified or detected could not be opened or read. :exc:`~dwave.cloud.exceptions.ConfigFileParseError`: Config file parse failed. """ # try loading configuration from a preferred new config subsystem # (`./dwave.conf`, `~/.config/dwave/dwave.conf`, etc) config = load_config(config_file=config_file, profile=profile, client=client, endpoint=endpoint, token=token, solver=solver, proxy=proxy) # fallback to legacy `.dwrc` if key variables missing if legacy_config_fallback and (not config.get('token') or not config.get('endpoint')): config = legacy_load_config(profile=profile, client=client, endpoint=endpoint, token=token, solver=solver, proxy=proxy) # manual override of other (client-custom) arguments config.update(kwargs) from dwave.cloud import qpu, sw _clients = {'qpu': qpu.Client, 'sw': sw.Client} _client = config.pop('client', None) or 'qpu' return _clients[_client](**config)
def from_config(cls, config_file=None, profile=None, client=None, endpoint=None, token=None, solver=None, proxy=None, legacy_config_fallback=True, **kwargs): """Client factory method to instantiate a client instance from configuration. Configuration files comply with standard Windows INI-like format, parsable with Python's :mod:`configparser`. An optional ``defaults`` section provides default key-value pairs for all other sections. User-defined key-value pairs (unrecognized keys) are passed through to the client. Configuration values can be specified in multiple ways, ranked in the following order (with 1 the highest ranked): 1. Values specified as keyword arguments in :func:`from_config()` 2. Values specified as environment variables 3. Values specified in the configuration file If the location of the configuration file is not specified, auto-detection searches for existing configuration files in the standard directories of :func:`get_configfile_paths`. If a configuration file explicitly specified, via an argument or environment variable, does not exist or is unreadable, loading fails with :exc:`~dwave.cloud.exceptions.ConfigFileReadError`. Loading fails with :exc:`~dwave.cloud.exceptions.ConfigFileParseError` if the file is readable but invalid as a configuration file. Similarly, if a profile explicitly specified, via an argument or environment variable, is not present in the loaded configuration, loading fails with :exc:`ValueError`. Explicit profile selection also fails if the configuration file is not explicitly specified, detected on the system, or defined via an environment variable. Environment variables: ``DWAVE_CONFIG_FILE``: Configuration file path used if no configuration file is specified. ``DWAVE_PROFILE``: Name of profile (section) to use if no profile is specified. ``DWAVE_API_CLIENT``: API client class used if no client is specified. Supported values are ``qpu`` or ``sw``. ``DWAVE_API_ENDPOINT``: API endpoint URL used if no endpoint is specified. ``DWAVE_API_TOKEN``: API authorization token used if no token is specified. ``DWAVE_API_SOLVER``: Default solver used if no solver is specified. ``DWAVE_API_PROXY``: URL for proxy connections to D-Wave API used if no proxy is specified. Args: config_file (str/[str]/None/False/True, default=None): Path to configuration file. If ``None``, the value is taken from ``DWAVE_CONFIG_FILE`` environment variable if defined. If the environment variable is undefined or empty, auto-detection searches for existing configuration files in the standard directories of :func:`get_configfile_paths`. If ``False``, loading from file is skipped. If ``True``, forces auto-detection (regardless of the ``DWAVE_CONFIG_FILE`` environment variable). profile (str, default=None): Profile name (name of the profile section in the configuration file). If undefined, inferred from ``DWAVE_PROFILE`` environment variable if defined. If the environment variable is undefined or empty, a profile is selected in the following order: 1. From the default section if it includes a profile key. 2. The first section (after the default section). 3. If no other section is defined besides ``[defaults]``, the defaults section is promoted and selected. client (str, default=None): Client type used for accessing the API. Supported values are ``qpu`` for :class:`dwave.cloud.qpu.Client` and ``sw`` for :class:`dwave.cloud.sw.Client`. endpoint (str, default=None): API endpoint URL. token (str, default=None): API authorization token. solver (str, default=None): Default :term:`solver` to use in :meth:`~dwave.cloud.client.Client.get_solver`. If undefined, :meth:`~dwave.cloud.client.Client.get_solver` will return the first solver available. proxy (str, default=None): URL for proxy to use in connections to D-Wave API. Can include username/password, port, scheme, etc. If undefined, client uses the system-level proxy, if defined, or connects directly to the API. legacy_config_fallback (bool, default=True): If True (the default) and loading from a standard D-Wave Cloud Client configuration file (``dwave.conf``) fails, tries loading a legacy configuration file (``~/.dwrc``). Other Parameters: Unrecognized keys (str): All unrecognized keys are passed through to the appropriate client class constructor as string keyword arguments. An explicit key value overrides an identical user-defined key value loaded from a configuration file. Returns: :class:`~dwave.cloud.client.Client` (:class:`dwave.cloud.qpu.Client` or :class:`dwave.cloud.sw.Client`, default=:class:`dwave.cloud.qpu.Client`): Appropriate instance of a QPU or software client. Raises: :exc:`~dwave.cloud.exceptions.ConfigFileReadError`: Config file specified or detected could not be opened or read. :exc:`~dwave.cloud.exceptions.ConfigFileParseError`: Config file parse failed. Examples: This first example initializes :class:`~dwave.cloud.client.Client` from an explicitly specified configuration file, "~/jane/my_path_to_config/my_cloud_conf.conf":: [defaults] endpoint = https://url.of.some.dwavesystem.com/sapi client = qpu token = ABC-123456789123456789123456789 [dw2000] solver = EXAMPLE_2000Q_SYSTEM token = DEF-987654321987654321987654321 The example code below creates a client object that connects to a D-Wave QPU, using :class:`dwave.cloud.qpu.Client` and ``EXAMPLE_2000Q_SYSTEM`` as a default solver. >>> from dwave.cloud import Client >>> client = Client.from_config(config_file='~/jane/my_path_to_config/my_cloud_conf.conf') # doctest: +SKIP >>> # code that uses client >>> client.close() This second example auto-detects a configuration file on the local system following the user/system configuration paths of :func:`get_configfile_paths`. It passes through to the instantiated client an unrecognized key-value pair my_param=`my_value`. >>> from dwave.cloud import Client >>> client = Client.from_config(my_param=`my_value`) >>> # code that uses client >>> client.close() This third example instantiates two clients, for managing both QPU and software solvers. Common key-value pairs are taken from the defaults section of a shared configuration file:: [defaults] endpoint = https://url.of.some.dwavesystem.com/sapi client = qpu [dw2000A] solver = EXAMPLE_2000Q_SYSTEM_A token = ABC-123456789123456789123456789 [sw_solver] client = sw solver = c4-sw_sample endpoint = https://url.of.some.software.resource.com/my_if token = DEF-987654321987654321987654321 [dw2000B] solver = EXAMPLE_2000Q_SYSTEM_B proxy = http://user:[email protected]:8080/ token = XYZ-0101010100112341234123412341234 The example code below creates client objects for two QPU solvers (at the same URL but each with its own solver ID and token) and one software solver. >>> from dwave.cloud import Client >>> client_qpu1 = Client.from_config(profile='dw2000A') # doctest: +SKIP >>> client_qpu1 = Client.from_config(profile='dw2000B') # doctest: +SKIP >>> client_sw1 = Client.from_config(profile='sw_solver') # doctest: +SKIP >>> client_qpu1.default_solver # doctest: +SKIP u'EXAMPLE_2000Q_SYSTEM_A' >>> client_qpu2.endpoint # doctest: +SKIP u'https://url.of.some.dwavesystem.com/sapi' >>> # code that uses client >>> client_qpu1.close() # doctest: +SKIP >>> client_qpu2.close() # doctest: +SKIP >>> client_sw1.close() # doctest: +SKIP This fourth example loads configurations auto-detected in more than one configuration file, with the higher priority file (in the current working directory) supplementing and overriding values from the lower priority user-local file. After instantiation, an endpoint from the default section and client from the profile section is provided from the user-local ``/usr/local/share/dwave/dwave.conf`` file:: [defaults] endpoint = https://int.se.dwavesystems.com/sapi [dw2000] client = qpu token = ABC-123456789123456789123456789 A solver is supplemented from the file in the current working directory, which also overrides the token value. ``./dwave.conf`` is the file in the current directory:: [dw2000] solver = EXAMPLE_2000Q_SYSTEM_A token = DEF-987654321987654321987654321 >>> from dwave.cloud import Client >>> client = Client.from_config() >>> client.default_solver # doctest: +SKIP u'EXAMPLE_2000Q_SYSTEM_A' >>> client.endpoint # doctest: +SKIP u'https://int.se.dwavesystems.com/sapi' >>> client.token # doctest: +SKIP u'DEF-987654321987654321987654321' >>> # code that uses client >>> client.close() # doctest: +SKIP """ # try loading configuration from a preferred new config subsystem # (`./dwave.conf`, `~/.config/dwave/dwave.conf`, etc) config = load_config(config_file=config_file, profile=profile, client=client, endpoint=endpoint, token=token, solver=solver, proxy=proxy) _LOGGER.debug("Config loaded: %r", config) # fallback to legacy `.dwrc` if key variables missing if legacy_config_fallback and (not config.get('token') or not config.get('endpoint')): config = legacy_load_config(profile=profile, client=client, endpoint=endpoint, token=token, solver=solver, proxy=proxy) _LOGGER.debug("Legacy config loaded: %r", config) # manual override of other (client-custom) arguments config.update(kwargs) from dwave.cloud import qpu, sw _clients = {'qpu': qpu.Client, 'sw': sw.Client, 'base': cls} _client = config.pop('client', None) or 'base' _LOGGER.debug("Final config used for %s.Client(): %r", _client, config) return _clients[_client](**config)