We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fail to produce a validation message on an array. validate_config_against_schema gets a TypeError Exception
<!---
Please complete as much of this form as possible. It's OK if you
don't know all the answers, but fill in as much as you can. This will help
us to help you. -->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report *
 - Feature Idea
 - Other

<!--- Is this actually a question? In that case, post it at forum.stackstorm.com. That's a great place
to interact with users and developers, and discuss ideas. -->

##### STACKSTORM VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "st2 --version" between quotes below -->
```
st2 2.8dev (0f1d9dc), on Python 2.7.6
```

##### OS / ENVIRONMENT / INSTALL METHOD
<!--- Below this comment, post what OS you are running this on, along with 
any other relevant information - e.g. Docker, Vagrant, Kubernetes, etc.
Describe how you installed ST2 - e.g. one-line install, custom install, etc -->

docker
##### SUMMARY
<!--- Explain the problem or request briefly -->

There is a problem when validating an array of objects. If the validation fails then on this line:

[https://github.com/StackStorm/st2/blob/master/st2common/st2common/util/pack.py#L112
](url)

Because the path has a number you cannot join an integer.


```
Traceback (most recent call last):
  File "/opt/stackstorm/st2/local/lib/python2.7/site-packages/st2common/router.py", line 472, in __call__
    resp = func(**kw)
  File "/opt/stackstorm/st2/local/lib/python2.7/site-packages/st2api/controllers/v1/pack_configs.py", line 107, in put
    config_api.validate(validate_against_schema=True)
  File "/opt/stackstorm/st2/local/lib/python2.7/site-packages/st2common/models/api/pack.py", line 274, in validate
    cleaned_values = self._validate_config_values_against_schema()
  File "/opt/stackstorm/st2/local/lib/python2.7/site-packages/st2common/models/api/pack.py", line 296, in _validate_config_values_against_schema
    pack_name=self.pack)
  File "/opt/stackstorm/st2/local/lib/python2.7/site-packages/st2common/util/pack.py", line 111, in validate_config_against_schema
    attribute = '.'.join(attribute)
TypeError: sequence item 1: expected string or Unicode, int found
2018-06-06 14:24:35,673 139995931735056 ERROR error_handling [-] API call failed: sequence item 1: expected string or Unicode, int found
Traceback (most recent call last):

```

When I changed the code to:

```
attribute = '.'.join(str(attribute))

```


MESSAGE: Failed validating attribute "d.e.q.u.e.(.[.u.'.i.n.s.t.a.n.c.e.s.'.,. .0.,. .u.'.a.l.i.a.s.'.].)" in config for pack "my_pack" (/opt/stackstorm/configs/my_pack.yaml): u'alias' is a required property.

I guess there is a problem with path when you have an array becuase you get for example 
instances[0]['alias'] for a path and the exception cannot produce a message






--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `st2common/st2common/util/pack.py`
Content:
```
1 # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 # contributor license agreements.  See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License.  You may obtain a copy of the License at
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 from __future__ import absolute_import
17 import os
18 import re
19 
20 from st2common.util import schema as util_schema
21 from st2common.constants.pack import MANIFEST_FILE_NAME
22 from st2common.constants.pack import PACK_REF_WHITELIST_REGEX
23 from st2common.content.loader import MetaLoader
24 from st2common.persistence.pack import Pack
25 
26 __all__ = [
27     'get_pack_ref_from_metadata',
28     'get_pack_metadata',
29 
30     'get_pack_common_libs_path_for_pack_ref',
31     'get_pack_common_libs_path_for_pack_db',
32 
33     'validate_config_against_schema',
34 
35     'normalize_pack_version'
36 ]
37 
38 
39 def get_pack_ref_from_metadata(metadata, pack_directory_name=None):
40     """
41     Utility function which retrieves pack "ref" attribute from the pack metadata file.
42 
43     If this attribute is not provided, an attempt is made to infer "ref" from the "name" attribute.
44 
45     :rtype: ``str``
46     """
47     pack_ref = None
48 
49     # The rules for the pack ref are as follows:
50     # 1. If ref attribute is available, we used that
51     # 2. If pack_directory_name is available we use that (this only applies to packs
52     # which are in sub-directories)
53     # 2. If attribute is not available, but pack name is and pack name meets the valid name
54     # criteria, we use that
55     if metadata.get('ref', None):
56         pack_ref = metadata['ref']
57     elif pack_directory_name and re.match(PACK_REF_WHITELIST_REGEX, pack_directory_name):
58         pack_ref = pack_directory_name
59     else:
60         if re.match(PACK_REF_WHITELIST_REGEX, metadata['name']):
61             pack_ref = metadata['name']
62         else:
63             msg = ('Pack name "%s" contains invalid characters and "ref" attribute is not '
64                    'available. You either need to add "ref" attribute which contains only word '
65                    'characters to the pack metadata file or update name attribute to contain only'
66                    'word characters.')
67             raise ValueError(msg % (metadata['name']))
68 
69     return pack_ref
70 
71 
72 def get_pack_metadata(pack_dir):
73     """
74     Return parsed metadata for a particular pack directory.
75 
76     :rtype: ``dict``
77     """
78     manifest_path = os.path.join(pack_dir, MANIFEST_FILE_NAME)
79 
80     if not os.path.isfile(manifest_path):
81         raise ValueError('Pack "%s" is missing %s file' % (pack_dir, MANIFEST_FILE_NAME))
82 
83     meta_loader = MetaLoader()
84     content = meta_loader.load(manifest_path)
85     if not content:
86         raise ValueError('Pack "%s" metadata file is empty' % (pack_dir))
87 
88     return content
89 
90 
91 def validate_config_against_schema(config_schema, config_object, config_path,
92                                   pack_name=None):
93     """
94     Validate provided config dictionary against the provided config schema
95     dictionary.
96     """
97     # NOTE: Lazy improt to avoid performance overhead of importing this module when it's not used
98     import jsonschema
99 
100     pack_name = pack_name or 'unknown'
101 
102     schema = util_schema.get_schema_for_resource_parameters(parameters_schema=config_schema,
103                                                             allow_additional_properties=True)
104     instance = config_object
105 
106     try:
107         cleaned = util_schema.validate(instance=instance, schema=schema,
108                                        cls=util_schema.CustomValidator, use_default=True,
109                                        allow_default_none=True)
110     except jsonschema.ValidationError as e:
111         attribute = getattr(e, 'path', [])
112         attribute = '.'.join(attribute)
113 
114         msg = ('Failed validating attribute "%s" in config for pack "%s" (%s): %s' %
115                (attribute, pack_name, config_path, str(e)))
116         raise jsonschema.ValidationError(msg)
117 
118     return cleaned
119 
120 
121 def get_pack_common_libs_path_for_pack_ref(pack_ref):
122     pack_db = Pack.get_by_ref(pack_ref)
123     pack_common_libs_path = get_pack_common_libs_path_for_pack_db(pack_db=pack_db)
124     return pack_common_libs_path
125 
126 
127 def get_pack_common_libs_path_for_pack_db(pack_db):
128     """
129     Return the pack's common lib path. This is the path where common code for sensors
130     and actions are placed.
131 
132     For example, if the pack is at /opt/stackstorm/packs/my_pack, you can place
133     common library code for actions and sensors in /opt/stackstorm/packs/my_pack/lib/.
134     This common library code is only available for python sensors and actions. The lib
135     structure also needs to follow a python convention with a __init__.py file.
136 
137     :param pack_db: Pack DB model
138     :type pack_db: :class:`PackDB`
139 
140     :rtype: ``str``
141     """
142     pack_dir = getattr(pack_db, 'path', None)
143 
144     if not pack_dir:
145         return None
146 
147     libs_path = os.path.join(pack_dir, 'lib')
148 
149     return libs_path
150 
151 
152 def normalize_pack_version(version):
153     """
154     Normalize old, pre StackStorm v2.1 non valid semver version string (e.g. 0.2) to a valid
155     semver version string (0.2.0).
156 
157     :rtype: ``str``
158     """
159     version = str(version)
160 
161     version_seperator_count = version.count('.')
162     if version_seperator_count == 1:
163         version = version + '.0'
164 
165     return version
166
```
--- 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/st2common/st2common/util/pack.py b/st2common/st2common/util/pack.py
--- a/st2common/st2common/util/pack.py
+++ b/st2common/st2common/util/pack.py
@@ -14,8 +14,10 @@
 # limitations under the License.
 
 from __future__ import absolute_import
+
 import os
 import re
+import collections
 
 from st2common.util import schema as util_schema
 from st2common.constants.pack import MANIFEST_FILE_NAME
@@ -109,7 +111,12 @@
                                        allow_default_none=True)
     except jsonschema.ValidationError as e:
         attribute = getattr(e, 'path', [])
-        attribute = '.'.join(attribute)
+
+        if isinstance(attribute, (tuple, list, collections.Iterable)):
+            attribute = [str(item) for item in attribute]
+            attribute = '.'.join(attribute)
+        else:
+            attribute = str(attribute)
 
         msg = ('Failed validating attribute "%s" in config for pack "%s" (%s): %s' %
                (attribute, pack_name, config_path, str(e)))

{"golden_diff": "diff --git a/st2common/st2common/util/pack.py b/st2common/st2common/util/pack.py\n--- a/st2common/st2common/util/pack.py\n+++ b/st2common/st2common/util/pack.py\n@@ -14,8 +14,10 @@\n # limitations under the License.\n \n from __future__ import absolute_import\n+\n import os\n import re\n+import collections\n \n from st2common.util import schema as util_schema\n from st2common.constants.pack import MANIFEST_FILE_NAME\n@@ -109,7 +111,12 @@\n                                        allow_default_none=True)\n     except jsonschema.ValidationError as e:\n         attribute = getattr(e, 'path', [])\n-        attribute = '.'.join(attribute)\n+\n+        if isinstance(attribute, (tuple, list, collections.Iterable)):\n+            attribute = [str(item) for item in attribute]\n+            attribute = '.'.join(attribute)\n+        else:\n+            attribute = str(attribute)\n \n         msg = ('Failed validating attribute \"%s\" in config for pack \"%s\" (%s): %s' %\n                (attribute, pack_name, config_path, str(e)))\n", "issue": "Fail to produce a validation message on an array. validate_config_against_schema gets a TypeError Exception\n<!---\r\nPlease complete as much of this form as possible. It's OK if you\r\ndon't know all the answers, but fill in as much as you can. This will help\r\nus to help you. -->\r\n\r\n##### ISSUE TYPE\r\n<!--- Pick one below and delete the rest -->\r\n - Bug Report *\r\n - Feature Idea\r\n - Other\r\n\r\n<!--- Is this actually a question? In that case, post it at forum.stackstorm.com. That's a great place\r\nto interact with users and developers, and discuss ideas. -->\r\n\r\n##### STACKSTORM VERSION\r\n<!--- Paste, BELOW THIS COMMENT, verbatim output from \"st2 --version\" between quotes below -->\r\n```\r\nst2 2.8dev (0f1d9dc), on Python 2.7.6\r\n```\r\n\r\n##### OS / ENVIRONMENT / INSTALL METHOD\r\n<!--- Below this comment, post what OS you are running this on, along with \r\nany other relevant information - e.g. Docker, Vagrant, Kubernetes, etc.\r\nDescribe how you installed ST2 - e.g. one-line install, custom install, etc -->\r\n\r\ndocker\r\n##### SUMMARY\r\n<!--- Explain the problem or request briefly -->\r\n\r\nThere is a problem when validating an array of objects. If the validation fails then on this line:\r\n\r\n[https://github.com/StackStorm/st2/blob/master/st2common/st2common/util/pack.py#L112\r\n](url)\r\n\r\nBecause the path has a number you cannot join an integer.\r\n\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"/opt/stackstorm/st2/local/lib/python2.7/site-packages/st2common/router.py\", line 472, in __call__\r\n    resp = func(**kw)\r\n  File \"/opt/stackstorm/st2/local/lib/python2.7/site-packages/st2api/controllers/v1/pack_configs.py\", line 107, in put\r\n    config_api.validate(validate_against_schema=True)\r\n  File \"/opt/stackstorm/st2/local/lib/python2.7/site-packages/st2common/models/api/pack.py\", line 274, in validate\r\n    cleaned_values = self._validate_config_values_against_schema()\r\n  File \"/opt/stackstorm/st2/local/lib/python2.7/site-packages/st2common/models/api/pack.py\", line 296, in _validate_config_values_against_schema\r\n    pack_name=self.pack)\r\n  File \"/opt/stackstorm/st2/local/lib/python2.7/site-packages/st2common/util/pack.py\", line 111, in validate_config_against_schema\r\n    attribute = '.'.join(attribute)\r\nTypeError: sequence item 1: expected string or Unicode, int found\r\n2018-06-06 14:24:35,673 139995931735056 ERROR error_handling [-] API call failed: sequence item 1: expected string or Unicode, int found\r\nTraceback (most recent call last):\r\n\r\n```\r\n\r\nWhen I changed the code to:\r\n\r\n```\r\nattribute = '.'.join(str(attribute))\r\n\r\n```\r\n\r\n\r\nMESSAGE: Failed validating attribute \"d.e.q.u.e.(.[.u.'.i.n.s.t.a.n.c.e.s.'.,. .0.,. .u.'.a.l.i.a.s.'.].)\" in config for pack \"my_pack\" (/opt/stackstorm/configs/my_pack.yaml): u'alias' is a required property.\r\n\r\nI guess there is a problem with path when you have an array becuase you get for example \r\ninstances[0]['alias'] for a path and the exception cannot produce a message\r\n\r\n\r\n\r\n\r\n\n", "before_files": [{"content": "# Licensed to the StackStorm, Inc ('StackStorm') under one or more\n# contributor license agreements.  See the NOTICE file distributed with\n# this work for additional information regarding copyright ownership.\n# The ASF licenses this file to You under the Apache License, Version 2.0\n# (the \"License\"); you may not use this file except in compliance with\n# the License.  You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nimport os\nimport re\n\nfrom st2common.util import schema as util_schema\nfrom st2common.constants.pack import MANIFEST_FILE_NAME\nfrom st2common.constants.pack import PACK_REF_WHITELIST_REGEX\nfrom st2common.content.loader import MetaLoader\nfrom st2common.persistence.pack import Pack\n\n__all__ = [\n    'get_pack_ref_from_metadata',\n    'get_pack_metadata',\n\n    'get_pack_common_libs_path_for_pack_ref',\n    'get_pack_common_libs_path_for_pack_db',\n\n    'validate_config_against_schema',\n\n    'normalize_pack_version'\n]\n\n\ndef get_pack_ref_from_metadata(metadata, pack_directory_name=None):\n    \"\"\"\n    Utility function which retrieves pack \"ref\" attribute from the pack metadata file.\n\n    If this attribute is not provided, an attempt is made to infer \"ref\" from the \"name\" attribute.\n\n    :rtype: ``str``\n    \"\"\"\n    pack_ref = None\n\n    # The rules for the pack ref are as follows:\n    # 1. If ref attribute is available, we used that\n    # 2. If pack_directory_name is available we use that (this only applies to packs\n    # which are in sub-directories)\n    # 2. If attribute is not available, but pack name is and pack name meets the valid name\n    # criteria, we use that\n    if metadata.get('ref', None):\n        pack_ref = metadata['ref']\n    elif pack_directory_name and re.match(PACK_REF_WHITELIST_REGEX, pack_directory_name):\n        pack_ref = pack_directory_name\n    else:\n        if re.match(PACK_REF_WHITELIST_REGEX, metadata['name']):\n            pack_ref = metadata['name']\n        else:\n            msg = ('Pack name \"%s\" contains invalid characters and \"ref\" attribute is not '\n                   'available. You either need to add \"ref\" attribute which contains only word '\n                   'characters to the pack metadata file or update name attribute to contain only'\n                   'word characters.')\n            raise ValueError(msg % (metadata['name']))\n\n    return pack_ref\n\n\ndef get_pack_metadata(pack_dir):\n    \"\"\"\n    Return parsed metadata for a particular pack directory.\n\n    :rtype: ``dict``\n    \"\"\"\n    manifest_path = os.path.join(pack_dir, MANIFEST_FILE_NAME)\n\n    if not os.path.isfile(manifest_path):\n        raise ValueError('Pack \"%s\" is missing %s file' % (pack_dir, MANIFEST_FILE_NAME))\n\n    meta_loader = MetaLoader()\n    content = meta_loader.load(manifest_path)\n    if not content:\n        raise ValueError('Pack \"%s\" metadata file is empty' % (pack_dir))\n\n    return content\n\n\ndef validate_config_against_schema(config_schema, config_object, config_path,\n                                  pack_name=None):\n    \"\"\"\n    Validate provided config dictionary against the provided config schema\n    dictionary.\n    \"\"\"\n    # NOTE: Lazy improt to avoid performance overhead of importing this module when it's not used\n    import jsonschema\n\n    pack_name = pack_name or 'unknown'\n\n    schema = util_schema.get_schema_for_resource_parameters(parameters_schema=config_schema,\n                                                            allow_additional_properties=True)\n    instance = config_object\n\n    try:\n        cleaned = util_schema.validate(instance=instance, schema=schema,\n                                       cls=util_schema.CustomValidator, use_default=True,\n                                       allow_default_none=True)\n    except jsonschema.ValidationError as e:\n        attribute = getattr(e, 'path', [])\n        attribute = '.'.join(attribute)\n\n        msg = ('Failed validating attribute \"%s\" in config for pack \"%s\" (%s): %s' %\n               (attribute, pack_name, config_path, str(e)))\n        raise jsonschema.ValidationError(msg)\n\n    return cleaned\n\n\ndef get_pack_common_libs_path_for_pack_ref(pack_ref):\n    pack_db = Pack.get_by_ref(pack_ref)\n    pack_common_libs_path = get_pack_common_libs_path_for_pack_db(pack_db=pack_db)\n    return pack_common_libs_path\n\n\ndef get_pack_common_libs_path_for_pack_db(pack_db):\n    \"\"\"\n    Return the pack's common lib path. This is the path where common code for sensors\n    and actions are placed.\n\n    For example, if the pack is at /opt/stackstorm/packs/my_pack, you can place\n    common library code for actions and sensors in /opt/stackstorm/packs/my_pack/lib/.\n    This common library code is only available for python sensors and actions. The lib\n    structure also needs to follow a python convention with a __init__.py file.\n\n    :param pack_db: Pack DB model\n    :type pack_db: :class:`PackDB`\n\n    :rtype: ``str``\n    \"\"\"\n    pack_dir = getattr(pack_db, 'path', None)\n\n    if not pack_dir:\n        return None\n\n    libs_path = os.path.join(pack_dir, 'lib')\n\n    return libs_path\n\n\ndef normalize_pack_version(version):\n    \"\"\"\n    Normalize old, pre StackStorm v2.1 non valid semver version string (e.g. 0.2) to a valid\n    semver version string (0.2.0).\n\n    :rtype: ``str``\n    \"\"\"\n    version = str(version)\n\n    version_seperator_count = version.count('.')\n    if version_seperator_count == 1:\n        version = version + '.0'\n\n    return version\n", "path": "st2common/st2common/util/pack.py"}], "after_files": [{"content": "# Licensed to the StackStorm, Inc ('StackStorm') under one or more\n# contributor license agreements.  See the NOTICE file distributed with\n# this work for additional information regarding copyright ownership.\n# The ASF licenses this file to You under the Apache License, Version 2.0\n# (the \"License\"); you may not use this file except in compliance with\n# the License.  You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\n\nimport os\nimport re\nimport collections\n\nfrom st2common.util import schema as util_schema\nfrom st2common.constants.pack import MANIFEST_FILE_NAME\nfrom st2common.constants.pack import PACK_REF_WHITELIST_REGEX\nfrom st2common.content.loader import MetaLoader\nfrom st2common.persistence.pack import Pack\n\n__all__ = [\n    'get_pack_ref_from_metadata',\n    'get_pack_metadata',\n\n    'get_pack_common_libs_path_for_pack_ref',\n    'get_pack_common_libs_path_for_pack_db',\n\n    'validate_config_against_schema',\n\n    'normalize_pack_version'\n]\n\n\ndef get_pack_ref_from_metadata(metadata, pack_directory_name=None):\n    \"\"\"\n    Utility function which retrieves pack \"ref\" attribute from the pack metadata file.\n\n    If this attribute is not provided, an attempt is made to infer \"ref\" from the \"name\" attribute.\n\n    :rtype: ``str``\n    \"\"\"\n    pack_ref = None\n\n    # The rules for the pack ref are as follows:\n    # 1. If ref attribute is available, we used that\n    # 2. If pack_directory_name is available we use that (this only applies to packs\n    # which are in sub-directories)\n    # 2. If attribute is not available, but pack name is and pack name meets the valid name\n    # criteria, we use that\n    if metadata.get('ref', None):\n        pack_ref = metadata['ref']\n    elif pack_directory_name and re.match(PACK_REF_WHITELIST_REGEX, pack_directory_name):\n        pack_ref = pack_directory_name\n    else:\n        if re.match(PACK_REF_WHITELIST_REGEX, metadata['name']):\n            pack_ref = metadata['name']\n        else:\n            msg = ('Pack name \"%s\" contains invalid characters and \"ref\" attribute is not '\n                   'available. You either need to add \"ref\" attribute which contains only word '\n                   'characters to the pack metadata file or update name attribute to contain only'\n                   'word characters.')\n            raise ValueError(msg % (metadata['name']))\n\n    return pack_ref\n\n\ndef get_pack_metadata(pack_dir):\n    \"\"\"\n    Return parsed metadata for a particular pack directory.\n\n    :rtype: ``dict``\n    \"\"\"\n    manifest_path = os.path.join(pack_dir, MANIFEST_FILE_NAME)\n\n    if not os.path.isfile(manifest_path):\n        raise ValueError('Pack \"%s\" is missing %s file' % (pack_dir, MANIFEST_FILE_NAME))\n\n    meta_loader = MetaLoader()\n    content = meta_loader.load(manifest_path)\n    if not content:\n        raise ValueError('Pack \"%s\" metadata file is empty' % (pack_dir))\n\n    return content\n\n\ndef validate_config_against_schema(config_schema, config_object, config_path,\n                                  pack_name=None):\n    \"\"\"\n    Validate provided config dictionary against the provided config schema\n    dictionary.\n    \"\"\"\n    # NOTE: Lazy improt to avoid performance overhead of importing this module when it's not used\n    import jsonschema\n\n    pack_name = pack_name or 'unknown'\n\n    schema = util_schema.get_schema_for_resource_parameters(parameters_schema=config_schema,\n                                                            allow_additional_properties=True)\n    instance = config_object\n\n    try:\n        cleaned = util_schema.validate(instance=instance, schema=schema,\n                                       cls=util_schema.CustomValidator, use_default=True,\n                                       allow_default_none=True)\n    except jsonschema.ValidationError as e:\n        attribute = getattr(e, 'path', [])\n\n        if isinstance(attribute, (tuple, list, collections.Iterable)):\n            attribute = [str(item) for item in attribute]\n            attribute = '.'.join(attribute)\n        else:\n            attribute = str(attribute)\n\n        msg = ('Failed validating attribute \"%s\" in config for pack \"%s\" (%s): %s' %\n               (attribute, pack_name, config_path, str(e)))\n        raise jsonschema.ValidationError(msg)\n\n    return cleaned\n\n\ndef get_pack_common_libs_path_for_pack_ref(pack_ref):\n    pack_db = Pack.get_by_ref(pack_ref)\n    pack_common_libs_path = get_pack_common_libs_path_for_pack_db(pack_db=pack_db)\n    return pack_common_libs_path\n\n\ndef get_pack_common_libs_path_for_pack_db(pack_db):\n    \"\"\"\n    Return the pack's common lib path. This is the path where common code for sensors\n    and actions are placed.\n\n    For example, if the pack is at /opt/stackstorm/packs/my_pack, you can place\n    common library code for actions and sensors in /opt/stackstorm/packs/my_pack/lib/.\n    This common library code is only available for python sensors and actions. The lib\n    structure also needs to follow a python convention with a __init__.py file.\n\n    :param pack_db: Pack DB model\n    :type pack_db: :class:`PackDB`\n\n    :rtype: ``str``\n    \"\"\"\n    pack_dir = getattr(pack_db, 'path', None)\n\n    if not pack_dir:\n        return None\n\n    libs_path = os.path.join(pack_dir, 'lib')\n\n    return libs_path\n\n\ndef normalize_pack_version(version):\n    \"\"\"\n    Normalize old, pre StackStorm v2.1 non valid semver version string (e.g. 0.2) to a valid\n    semver version string (0.2.0).\n\n    :rtype: ``str``\n    \"\"\"\n    version = str(version)\n\n    version_seperator_count = version.count('.')\n    if version_seperator_count == 1:\n        version = version + '.0'\n\n    return version\n", "path": "st2common/st2common/util/pack.py"}]}