We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
molecule does not honour --destroy=never and prunes ephemeral data anyway (appeared after #1739)
# Issue Type

- Bug report

# Molecule and Ansible details

```
ansible --version && molecule --version
ansible 2.7.10
  config file = None
  configured module search path = [u'/home/jenkins/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Jun 20 2019, 20:27:34) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
molecule, version 2.22
```

Molecule installation method (one of):

- pip

Ansible installation method (one of):

- pip

Detail any linters or test runners used:

N/A

# Desired Behaviour

When running _molecule test --destroy=never_ do not prune molecule ephemeral data, even when _destroy_ stage is a part of a scenario.

# Actual Behaviour

Molecule configuration excerpt:
```
scenario:
  name: default
  test_sequence:
  - dependency
  - syntax
  - lint
  - create
  - converge
  - idempotence
  - verify
  - destroy
```

_molecule test --destroy=never --all_ does not honour _--destroy=never_ and prunes ephemeral data when _destroy_ is a part of a scenario in the molecule configuration.

Output excerpt:
```
Verifier completed successfully.
--> Scenario: 'default'
--> Action: 'destroy'
Skipping, '--destroy=never' requested.
--> Pruning extra files from scenario ephemeral directory
```

Described behaviour contradicts the purpose of NEVER destroying an environment parameter regardless of what stages were defined in the scenario.
In fact, the environment is left intact, though ephemeral data is gone, hence environment becomes unattended and it's not possible to destroy it later.

_molecule test --destroy=never --all_
_molecule destroy --all_ <-- will not destroy the environment


Previous molecule version works as expected:
```
ansible 2.7.10
  config file = None
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Jun 20 2019, 20:27:34) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
molecule, version 2.20.1
```

Output excerpt
```
Verifier completed successfully.
--> Scenario: 'default'
--> Action: 'destroy'
Skipping, '--destroy=never' requested.
```

Pruning based on presence of _destroy_ stage in the molecule configuration appeared after https://github.com/ansible/molecule/pull/1739


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `molecule/command/base.py`
Content:
```
1 #  Copyright (c) 2015-2018 Cisco Systems, Inc.
2 #
3 #  Permission is hereby granted, free of charge, to any person obtaining a copy
4 #  of this software and associated documentation files (the "Software"), to
5 #  deal in the Software without restriction, including without limitation the
6 #  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 #  sell copies of the Software, and to permit persons to whom the Software is
8 #  furnished to do so, subject to the following conditions:
9 #
10 #  The above copyright notice and this permission notice shall be included in
11 #  all copies or substantial portions of the Software.
12 #
13 #  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 #  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 #  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 #  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 #  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 #  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 #  DEALINGS IN THE SOFTWARE.
20 
21 import abc
22 import collections
23 import glob
24 import os
25 
26 import six
27 
28 import molecule.command
29 import molecule.scenarios
30 from molecule import config
31 from molecule import logger
32 from molecule import util
33 
34 LOG = logger.get_logger(__name__)
35 MOLECULE_GLOB = os.environ.get('MOLECULE_GLOB', 'molecule/*/molecule.yml')
36 MOLECULE_DEFAULT_SCENARIO_NAME = 'default'
37 
38 
39 @six.add_metaclass(abc.ABCMeta)
40 class Base(object):
41     """
42     An abstract base class used to define the command interface.
43     """
44 
45     def __init__(self, c):
46         """
47         Base initializer for all :ref:`Command` classes.
48 
49         :param c: An instance of a Molecule config.
50         :returns: None
51         """
52         self._config = c
53         self._setup()
54 
55     @abc.abstractmethod
56     def execute(self):  # pragma: no cover
57         pass
58 
59     def print_info(self):
60         msg = "Scenario: '{}'".format(self._config.scenario.name)
61         LOG.info(msg)
62         msg = "Action: '{}'".format(util.underscore(self.__class__.__name__))
63         LOG.info(msg)
64 
65     def _setup(self):
66         """
67         Prepare Molecule's provisioner and returns None.
68 
69         :return: None
70         """
71         self._config.provisioner.write_config()
72         self._config.provisioner.manage_inventory()
73 
74 
75 def execute_cmdline_scenarios(scenario_name, args, command_args, ansible_args=()):
76     """
77     Execute scenario sequences based on parsed command-line arguments.
78 
79     This is useful for subcommands that run scenario sequences, which
80     excludes subcommands such as ``list``, ``login``, and ``matrix``.
81 
82     ``args`` and ``command_args`` are combined using :func:`get_configs`
83     to generate the scenario(s) configuration.
84 
85     :param scenario_name: Name of scenario to run, or ``None`` to run all.
86     :param args: ``args`` dict from ``click`` command context
87     :param command_args: dict of command argumentss, including the target
88                          subcommand to execute
89     :returns: None
90 
91     """
92     scenarios = molecule.scenarios.Scenarios(
93         get_configs(args, command_args, ansible_args), scenario_name
94     )
95     scenarios.print_matrix()
96     for scenario in scenarios:
97         try:
98             execute_scenario(scenario)
99         except SystemExit:
100             # if the command has a 'destroy' arg, like test does,
101             # handle that behavior here.
102             if command_args.get('destroy') == 'always':
103                 msg = (
104                     'An error occurred during the {} sequence action: '
105                     "'{}'. Cleaning up."
106                 ).format(scenario.config.subcommand, scenario.config.action)
107                 LOG.warning(msg)
108                 execute_subcommand(scenario.config, 'cleanup')
109                 execute_subcommand(scenario.config, 'destroy')
110                 # always prune ephemeral dir if destroying on failure
111                 scenario.prune()
112                 if scenario.config.is_parallel:
113                     scenario._remove_scenario_state_directory()
114                 util.sysexit()
115             else:
116                 raise
117 
118 
119 def execute_subcommand(config, subcommand):
120     command_module = getattr(molecule.command, subcommand)
121     command = getattr(command_module, util.camelize(subcommand))
122     # knowledge of the current action is used by some provisioners
123     # to ensure they behave correctly during certain sequence steps,
124     # particulary the setting of ansible options in create/destroy,
125     # and is also used for reporting in execute_cmdline_scenarios
126     config.action = subcommand
127 
128     return command(config).execute()
129 
130 
131 def execute_scenario(scenario):
132     """
133     Execute each command in the given scenario's configured sequence.
134 
135     :param scenario: The scenario to execute.
136     :returns: None
137 
138     """
139 
140     for action in scenario.sequence:
141         execute_subcommand(scenario.config, action)
142 
143     # pruning only if a 'destroy' step was in the sequence allows for normal
144     # debugging by manually stepping through a scenario sequence
145     if 'destroy' in scenario.sequence:
146         scenario.prune()
147 
148         if scenario.config.is_parallel:
149             scenario._remove_scenario_state_directory()
150 
151 
152 def get_configs(args, command_args, ansible_args=()):
153     """
154     Glob the current directory for Molecule config files, instantiate config
155     objects, and returns a list.
156 
157     :param args: A dict of options, arguments and commands from the CLI.
158     :param command_args: A dict of options passed to the subcommand from
159      the CLI.
160     :param ansible_args: An optional tuple of arguments provided to the
161      `ansible-playbook` command.
162     :return: list
163     """
164     configs = [
165         config.Config(
166             molecule_file=util.abs_path(c),
167             args=args,
168             command_args=command_args,
169             ansible_args=ansible_args,
170         )
171         for c in glob.glob(MOLECULE_GLOB)
172     ]
173     _verify_configs(configs)
174 
175     return configs
176 
177 
178 def _verify_configs(configs):
179     """
180     Verify a Molecule config was found and returns None.
181 
182     :param configs: A list containing absolute paths to Molecule config files.
183     :return: None
184     """
185     if configs:
186         scenario_names = [c.scenario.name for c in configs]
187         for scenario_name, n in collections.Counter(scenario_names).items():
188             if n > 1:
189                 msg = ("Duplicate scenario name '{}' found.  " 'Exiting.').format(
190                     scenario_name
191                 )
192                 util.sysexit_with_message(msg)
193 
194     else:
195         msg = "'{}' glob failed.  Exiting.".format(MOLECULE_GLOB)
196         util.sysexit_with_message(msg)
197 
198 
199 def _get_subcommand(string):
200     return string.split('.')[-1]
201
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/molecule/command/base.py b/molecule/command/base.py
--- a/molecule/command/base.py
+++ b/molecule/command/base.py
@@ -140,9 +140,10 @@
     for action in scenario.sequence:
         execute_subcommand(scenario.config, action)
 
-    # pruning only if a 'destroy' step was in the sequence allows for normal
-    # debugging by manually stepping through a scenario sequence
-    if 'destroy' in scenario.sequence:
+    if (
+        'destroy' in scenario.sequence
+        and scenario.config.command_args.get('destroy') != 'never'
+    ):
         scenario.prune()
 
         if scenario.config.is_parallel:

{"golden_diff": "diff --git a/molecule/command/base.py b/molecule/command/base.py\n--- a/molecule/command/base.py\n+++ b/molecule/command/base.py\n@@ -140,9 +140,10 @@\n     for action in scenario.sequence:\n         execute_subcommand(scenario.config, action)\n \n-    # pruning only if a 'destroy' step was in the sequence allows for normal\n-    # debugging by manually stepping through a scenario sequence\n-    if 'destroy' in scenario.sequence:\n+    if (\n+        'destroy' in scenario.sequence\n+        and scenario.config.command_args.get('destroy') != 'never'\n+    ):\n         scenario.prune()\n \n         if scenario.config.is_parallel:\n", "issue": "molecule does not honour --destroy=never and prunes ephemeral data anyway (appeared after #1739)\n# Issue Type\r\n\r\n- Bug report\r\n\r\n# Molecule and Ansible details\r\n\r\n```\r\nansible --version && molecule --version\r\nansible 2.7.10\r\n  config file = None\r\n  configured module search path = [u'/home/jenkins/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']\r\n  ansible python module location = /usr/lib/python2.7/site-packages/ansible\r\n  executable location = /usr/bin/ansible\r\n  python version = 2.7.5 (default, Jun 20 2019, 20:27:34) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]\r\nmolecule, version 2.22\r\n```\r\n\r\nMolecule installation method (one of):\r\n\r\n- pip\r\n\r\nAnsible installation method (one of):\r\n\r\n- pip\r\n\r\nDetail any linters or test runners used:\r\n\r\nN/A\r\n\r\n# Desired Behaviour\r\n\r\nWhen running _molecule test --destroy=never_ do not prune molecule ephemeral data, even when _destroy_ stage is a part of a scenario.\r\n\r\n# Actual Behaviour\r\n\r\nMolecule configuration excerpt:\r\n```\r\nscenario:\r\n  name: default\r\n  test_sequence:\r\n  - dependency\r\n  - syntax\r\n  - lint\r\n  - create\r\n  - converge\r\n  - idempotence\r\n  - verify\r\n  - destroy\r\n```\r\n\r\n_molecule test --destroy=never --all_ does not honour _--destroy=never_ and prunes ephemeral data when _destroy_ is a part of a scenario in the molecule configuration.\r\n\r\nOutput excerpt:\r\n```\r\nVerifier completed successfully.\r\n--> Scenario: 'default'\r\n--> Action: 'destroy'\r\nSkipping, '--destroy=never' requested.\r\n--> Pruning extra files from scenario ephemeral directory\r\n```\r\n\r\nDescribed behaviour contradicts the purpose of NEVER destroying an environment parameter regardless of what stages were defined in the scenario.\r\nIn fact, the environment is left intact, though ephemeral data is gone, hence environment becomes unattended and it's not possible to destroy it later.\r\n\r\n_molecule test --destroy=never --all_\r\n_molecule destroy --all_ <-- will not destroy the environment\r\n\r\n\r\nPrevious molecule version works as expected:\r\n```\r\nansible 2.7.10\r\n  config file = None\r\n  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']\r\n  ansible python module location = /usr/lib/python2.7/site-packages/ansible\r\n  executable location = /usr/bin/ansible\r\n  python version = 2.7.5 (default, Jun 20 2019, 20:27:34) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]\r\nmolecule, version 2.20.1\r\n```\r\n\r\nOutput excerpt\r\n```\r\nVerifier completed successfully.\r\n--> Scenario: 'default'\r\n--> Action: 'destroy'\r\nSkipping, '--destroy=never' requested.\r\n```\r\n\r\nPruning based on presence of _destroy_ stage in the molecule configuration appeared after https://github.com/ansible/molecule/pull/1739\r\n\n", "before_files": [{"content": "#  Copyright (c) 2015-2018 Cisco Systems, Inc.\n#\n#  Permission is hereby granted, free of charge, to any person obtaining a copy\n#  of this software and associated documentation files (the \"Software\"), to\n#  deal in the Software without restriction, including without limitation the\n#  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n#  sell copies of the Software, and to permit persons to whom the Software is\n#  furnished to do so, subject to the following conditions:\n#\n#  The above copyright notice and this permission notice shall be included in\n#  all copies or substantial portions of the Software.\n#\n#  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n#  DEALINGS IN THE SOFTWARE.\n\nimport abc\nimport collections\nimport glob\nimport os\n\nimport six\n\nimport molecule.command\nimport molecule.scenarios\nfrom molecule import config\nfrom molecule import logger\nfrom molecule import util\n\nLOG = logger.get_logger(__name__)\nMOLECULE_GLOB = os.environ.get('MOLECULE_GLOB', 'molecule/*/molecule.yml')\nMOLECULE_DEFAULT_SCENARIO_NAME = 'default'\n\n\n@six.add_metaclass(abc.ABCMeta)\nclass Base(object):\n    \"\"\"\n    An abstract base class used to define the command interface.\n    \"\"\"\n\n    def __init__(self, c):\n        \"\"\"\n        Base initializer for all :ref:`Command` classes.\n\n        :param c: An instance of a Molecule config.\n        :returns: None\n        \"\"\"\n        self._config = c\n        self._setup()\n\n    @abc.abstractmethod\n    def execute(self):  # pragma: no cover\n        pass\n\n    def print_info(self):\n        msg = \"Scenario: '{}'\".format(self._config.scenario.name)\n        LOG.info(msg)\n        msg = \"Action: '{}'\".format(util.underscore(self.__class__.__name__))\n        LOG.info(msg)\n\n    def _setup(self):\n        \"\"\"\n        Prepare Molecule's provisioner and returns None.\n\n        :return: None\n        \"\"\"\n        self._config.provisioner.write_config()\n        self._config.provisioner.manage_inventory()\n\n\ndef execute_cmdline_scenarios(scenario_name, args, command_args, ansible_args=()):\n    \"\"\"\n    Execute scenario sequences based on parsed command-line arguments.\n\n    This is useful for subcommands that run scenario sequences, which\n    excludes subcommands such as ``list``, ``login``, and ``matrix``.\n\n    ``args`` and ``command_args`` are combined using :func:`get_configs`\n    to generate the scenario(s) configuration.\n\n    :param scenario_name: Name of scenario to run, or ``None`` to run all.\n    :param args: ``args`` dict from ``click`` command context\n    :param command_args: dict of command argumentss, including the target\n                         subcommand to execute\n    :returns: None\n\n    \"\"\"\n    scenarios = molecule.scenarios.Scenarios(\n        get_configs(args, command_args, ansible_args), scenario_name\n    )\n    scenarios.print_matrix()\n    for scenario in scenarios:\n        try:\n            execute_scenario(scenario)\n        except SystemExit:\n            # if the command has a 'destroy' arg, like test does,\n            # handle that behavior here.\n            if command_args.get('destroy') == 'always':\n                msg = (\n                    'An error occurred during the {} sequence action: '\n                    \"'{}'. Cleaning up.\"\n                ).format(scenario.config.subcommand, scenario.config.action)\n                LOG.warning(msg)\n                execute_subcommand(scenario.config, 'cleanup')\n                execute_subcommand(scenario.config, 'destroy')\n                # always prune ephemeral dir if destroying on failure\n                scenario.prune()\n                if scenario.config.is_parallel:\n                    scenario._remove_scenario_state_directory()\n                util.sysexit()\n            else:\n                raise\n\n\ndef execute_subcommand(config, subcommand):\n    command_module = getattr(molecule.command, subcommand)\n    command = getattr(command_module, util.camelize(subcommand))\n    # knowledge of the current action is used by some provisioners\n    # to ensure they behave correctly during certain sequence steps,\n    # particulary the setting of ansible options in create/destroy,\n    # and is also used for reporting in execute_cmdline_scenarios\n    config.action = subcommand\n\n    return command(config).execute()\n\n\ndef execute_scenario(scenario):\n    \"\"\"\n    Execute each command in the given scenario's configured sequence.\n\n    :param scenario: The scenario to execute.\n    :returns: None\n\n    \"\"\"\n\n    for action in scenario.sequence:\n        execute_subcommand(scenario.config, action)\n\n    # pruning only if a 'destroy' step was in the sequence allows for normal\n    # debugging by manually stepping through a scenario sequence\n    if 'destroy' in scenario.sequence:\n        scenario.prune()\n\n        if scenario.config.is_parallel:\n            scenario._remove_scenario_state_directory()\n\n\ndef get_configs(args, command_args, ansible_args=()):\n    \"\"\"\n    Glob the current directory for Molecule config files, instantiate config\n    objects, and returns a list.\n\n    :param args: A dict of options, arguments and commands from the CLI.\n    :param command_args: A dict of options passed to the subcommand from\n     the CLI.\n    :param ansible_args: An optional tuple of arguments provided to the\n     `ansible-playbook` command.\n    :return: list\n    \"\"\"\n    configs = [\n        config.Config(\n            molecule_file=util.abs_path(c),\n            args=args,\n            command_args=command_args,\n            ansible_args=ansible_args,\n        )\n        for c in glob.glob(MOLECULE_GLOB)\n    ]\n    _verify_configs(configs)\n\n    return configs\n\n\ndef _verify_configs(configs):\n    \"\"\"\n    Verify a Molecule config was found and returns None.\n\n    :param configs: A list containing absolute paths to Molecule config files.\n    :return: None\n    \"\"\"\n    if configs:\n        scenario_names = [c.scenario.name for c in configs]\n        for scenario_name, n in collections.Counter(scenario_names).items():\n            if n > 1:\n                msg = (\"Duplicate scenario name '{}' found.  \" 'Exiting.').format(\n                    scenario_name\n                )\n                util.sysexit_with_message(msg)\n\n    else:\n        msg = \"'{}' glob failed.  Exiting.\".format(MOLECULE_GLOB)\n        util.sysexit_with_message(msg)\n\n\ndef _get_subcommand(string):\n    return string.split('.')[-1]\n", "path": "molecule/command/base.py"}], "after_files": [{"content": "#  Copyright (c) 2015-2018 Cisco Systems, Inc.\n#\n#  Permission is hereby granted, free of charge, to any person obtaining a copy\n#  of this software and associated documentation files (the \"Software\"), to\n#  deal in the Software without restriction, including without limitation the\n#  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n#  sell copies of the Software, and to permit persons to whom the Software is\n#  furnished to do so, subject to the following conditions:\n#\n#  The above copyright notice and this permission notice shall be included in\n#  all copies or substantial portions of the Software.\n#\n#  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n#  DEALINGS IN THE SOFTWARE.\n\nimport abc\nimport collections\nimport glob\nimport os\n\nimport six\n\nimport molecule.command\nimport molecule.scenarios\nfrom molecule import config\nfrom molecule import logger\nfrom molecule import util\n\nLOG = logger.get_logger(__name__)\nMOLECULE_GLOB = os.environ.get('MOLECULE_GLOB', 'molecule/*/molecule.yml')\nMOLECULE_DEFAULT_SCENARIO_NAME = 'default'\n\n\n@six.add_metaclass(abc.ABCMeta)\nclass Base(object):\n    \"\"\"\n    An abstract base class used to define the command interface.\n    \"\"\"\n\n    def __init__(self, c):\n        \"\"\"\n        Base initializer for all :ref:`Command` classes.\n\n        :param c: An instance of a Molecule config.\n        :returns: None\n        \"\"\"\n        self._config = c\n        self._setup()\n\n    @abc.abstractmethod\n    def execute(self):  # pragma: no cover\n        pass\n\n    def print_info(self):\n        msg = \"Scenario: '{}'\".format(self._config.scenario.name)\n        LOG.info(msg)\n        msg = \"Action: '{}'\".format(util.underscore(self.__class__.__name__))\n        LOG.info(msg)\n\n    def _setup(self):\n        \"\"\"\n        Prepare Molecule's provisioner and returns None.\n\n        :return: None\n        \"\"\"\n        self._config.provisioner.write_config()\n        self._config.provisioner.manage_inventory()\n\n\ndef execute_cmdline_scenarios(scenario_name, args, command_args, ansible_args=()):\n    \"\"\"\n    Execute scenario sequences based on parsed command-line arguments.\n\n    This is useful for subcommands that run scenario sequences, which\n    excludes subcommands such as ``list``, ``login``, and ``matrix``.\n\n    ``args`` and ``command_args`` are combined using :func:`get_configs`\n    to generate the scenario(s) configuration.\n\n    :param scenario_name: Name of scenario to run, or ``None`` to run all.\n    :param args: ``args`` dict from ``click`` command context\n    :param command_args: dict of command argumentss, including the target\n                         subcommand to execute\n    :returns: None\n\n    \"\"\"\n    scenarios = molecule.scenarios.Scenarios(\n        get_configs(args, command_args, ansible_args), scenario_name\n    )\n    scenarios.print_matrix()\n    for scenario in scenarios:\n        try:\n            execute_scenario(scenario)\n        except SystemExit:\n            # if the command has a 'destroy' arg, like test does,\n            # handle that behavior here.\n            if command_args.get('destroy') == 'always':\n                msg = (\n                    'An error occurred during the {} sequence action: '\n                    \"'{}'. Cleaning up.\"\n                ).format(scenario.config.subcommand, scenario.config.action)\n                LOG.warning(msg)\n                execute_subcommand(scenario.config, 'cleanup')\n                execute_subcommand(scenario.config, 'destroy')\n                # always prune ephemeral dir if destroying on failure\n                scenario.prune()\n                if scenario.config.is_parallel:\n                    scenario._remove_scenario_state_directory()\n                util.sysexit()\n            else:\n                raise\n\n\ndef execute_subcommand(config, subcommand):\n    command_module = getattr(molecule.command, subcommand)\n    command = getattr(command_module, util.camelize(subcommand))\n    # knowledge of the current action is used by some provisioners\n    # to ensure they behave correctly during certain sequence steps,\n    # particulary the setting of ansible options in create/destroy,\n    # and is also used for reporting in execute_cmdline_scenarios\n    config.action = subcommand\n\n    return command(config).execute()\n\n\ndef execute_scenario(scenario):\n    \"\"\"\n    Execute each command in the given scenario's configured sequence.\n\n    :param scenario: The scenario to execute.\n    :returns: None\n\n    \"\"\"\n\n    for action in scenario.sequence:\n        execute_subcommand(scenario.config, action)\n\n    if (\n        'destroy' in scenario.sequence\n        and scenario.config.command_args.get('destroy') != 'never'\n    ):\n        scenario.prune()\n\n        if scenario.config.is_parallel:\n            scenario._remove_scenario_state_directory()\n\n\ndef get_configs(args, command_args, ansible_args=()):\n    \"\"\"\n    Glob the current directory for Molecule config files, instantiate config\n    objects, and returns a list.\n\n    :param args: A dict of options, arguments and commands from the CLI.\n    :param command_args: A dict of options passed to the subcommand from\n     the CLI.\n    :param ansible_args: An optional tuple of arguments provided to the\n     `ansible-playbook` command.\n    :return: list\n    \"\"\"\n    configs = [\n        config.Config(\n            molecule_file=util.abs_path(c),\n            args=args,\n            command_args=command_args,\n            ansible_args=ansible_args,\n        )\n        for c in glob.glob(MOLECULE_GLOB)\n    ]\n    _verify_configs(configs)\n\n    return configs\n\n\ndef _verify_configs(configs):\n    \"\"\"\n    Verify a Molecule config was found and returns None.\n\n    :param configs: A list containing absolute paths to Molecule config files.\n    :return: None\n    \"\"\"\n    if configs:\n        scenario_names = [c.scenario.name for c in configs]\n        for scenario_name, n in collections.Counter(scenario_names).items():\n            if n > 1:\n                msg = (\"Duplicate scenario name '{}' found.  \" 'Exiting.').format(\n                    scenario_name\n                )\n                util.sysexit_with_message(msg)\n\n    else:\n        msg = \"'{}' glob failed.  Exiting.\".format(MOLECULE_GLOB)\n        util.sysexit_with_message(msg)\n\n\ndef _get_subcommand(string):\n    return string.split('.')[-1]\n", "path": "molecule/command/base.py"}]}