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)
Esempio n. 2
0
    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)
Esempio n. 3
0
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()
Esempio n. 4
0
    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)
Esempio n. 5
0
    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)