We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ImpalaTable.describe_formatted may throw an exception
excerpt. Will investigate soon

``` python
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-115-87d40e40503e> in <module>()
----> 1 repr(db.csv_as_parquet.describe_formatted())

/home/wesm/code/cloudera/ibis/ibis/impala/metadata.py in __repr__(self)
    295             data['partition schema'] = self.partitions
    296 
--> 297         pprint.pprint(data, stream=buf)
    298 
    299         return buf.getvalue()

/home/wesm/anaconda3/lib/python3.5/pprint.py in pprint(object, stream, indent, width, depth, compact)
     51         stream=stream, indent=indent, width=width, depth=depth,
     52         compact=compact)
---> 53     printer.pprint(object)
     54 
     55 def pformat(object, indent=1, width=80, depth=None, *, compact=False):
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ibis/impala/metadata.py`
Content:
```
1 # Copyright 2014 Cloudera Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 from six import StringIO
16 import pandas as pd
17 
18 
19 def parse_metadata(descr_table):
20     parser = MetadataParser(descr_table)
21     return parser.parse()
22 
23 
24 def _noop(tup):
25     return None
26 
27 
28 def _item_converter(i):
29     def _get_item(converter=None):
30         def _converter(tup):
31             result = tup[i]
32             if converter is not None:
33                 result = converter(result)
34             return result
35 
36         return _converter
37 
38     return _get_item
39 
40 _get_type = _item_converter(1)
41 _get_comment = _item_converter(2)
42 
43 
44 def _try_timestamp(x):
45     try:
46         return pd.Timestamp(x)
47     except (ValueError, TypeError):
48         return x
49 
50 
51 def _try_unix_timestamp(x):
52     try:
53         return pd.Timestamp.fromtimestamp(int(x))
54     except (ValueError, TypeError):
55         return x
56 
57 
58 def _try_boolean(x):
59     try:
60         x = x.lower()
61         if x in ('true', 'yes'):
62             return True
63         elif x in ('false', 'no'):
64             return False
65         return x
66     except (ValueError, TypeError):
67         return x
68 
69 
70 def _try_int(x):
71     try:
72         return int(x)
73     except (ValueError, TypeError):
74         return x
75 
76 
77 class MetadataParser(object):
78 
79     """
80     A simple state-ish machine to parse the results of DESCRIBE FORMATTED
81     """
82 
83     def __init__(self, table):
84         self.table = table
85         self.tuples = list(self.table.itertuples(index=False))
86 
87     def _reset(self):
88         self.pos = 0
89         self.schema = None
90         self.partitions = None
91         self.info = None
92         self.storage = None
93 
94     def _next_tuple(self):
95         if self.pos == len(self.tuples):
96             raise StopIteration
97 
98         result = self.tuples[self.pos]
99         self.pos += 1
100         return result
101 
102     def parse(self):
103         self._reset()
104         self._parse()
105 
106         return TableMetadata(self.schema, self.info, self.storage,
107                              partitions=self.partitions)
108 
109     def _parse(self):
110         self.schema = self._parse_schema()
111 
112         next_section = self._next_tuple()
113         if 'partition' in next_section[0].lower():
114             self._parse_partitions()
115         else:
116             self._parse_info()
117 
118     def _parse_partitions(self):
119         self.partitions = self._parse_schema()
120 
121         next_section = self._next_tuple()
122         if 'table information' not in next_section[0].lower():
123             raise ValueError('Table information not present')
124 
125         self._parse_info()
126 
127     def _parse_schema(self):
128         tup = self._next_tuple()
129         if 'col_name' not in tup[0]:
130             raise ValueError('DESCRIBE FORMATTED did not return '
131                              'the expected results: {0}'
132                              .format(tup))
133         self._next_tuple()
134 
135         # Use for both main schema and partition schema (if any)
136         schema = []
137         while True:
138             tup = self._next_tuple()
139             if tup[0].strip() == '':
140                 break
141             schema.append((tup[0], tup[1]))
142 
143         return schema
144 
145     def _parse_info(self):
146         self.info = {}
147         while True:
148             tup = self._next_tuple()
149             orig_key = tup[0].strip(':')
150             key = _clean_param_name(tup[0])
151 
152             if key == '' or key.startswith('#'):
153                 # section is done
154                 break
155 
156             if key == 'table parameters':
157                 self._parse_table_parameters()
158             elif key in self._info_cleaners:
159                 result = self._info_cleaners[key](tup)
160                 self.info[orig_key] = result
161             else:
162                 self.info[orig_key] = tup[1]
163 
164         if 'storage information' not in key:
165             raise ValueError('Storage information not present')
166 
167         self._parse_storage_info()
168 
169     _info_cleaners = {
170         'database': _get_type(),
171         'owner': _get_type(),
172         'createtime': _get_type(_try_timestamp),
173         'lastaccesstime': _get_type(_try_timestamp),
174         'protect mode': _get_type(),
175         'retention': _get_type(_try_int),
176         'location': _get_type(),
177         'table type': _get_type()
178     }
179 
180     def _parse_table_parameters(self):
181         params = self._parse_nested_params(self._table_param_cleaners)
182         self.info['Table Parameters'] = params
183 
184     _table_param_cleaners = {
185         'external': _try_boolean,
186         'column_stats_accurate': _try_boolean,
187         'numfiles': _try_int,
188         'totalsize': _try_int,
189         'stats_generated_via_stats_task': _try_boolean,
190         'numrows': _try_int,
191         'transient_lastddltime': _try_unix_timestamp,
192     }
193 
194     def _parse_storage_info(self):
195         self.storage = {}
196         while True:
197             # end of the road
198             try:
199                 tup = self._next_tuple()
200             except StopIteration:
201                 break
202 
203             orig_key = tup[0].strip(':')
204             key = _clean_param_name(tup[0])
205 
206             if key == '' or key.startswith('#'):
207                 # section is done
208                 break
209 
210             if key == 'storage desc params':
211                 self._parse_storage_desc_params()
212             elif key in self._storage_cleaners:
213                 result = self._storage_cleaners[key](tup)
214                 self.storage[orig_key] = result
215             else:
216                 self.storage[orig_key] = tup[1]
217 
218     _storage_cleaners = {
219         'compressed': _get_type(_try_boolean),
220         'num buckets': _get_type(_try_int),
221     }
222 
223     def _parse_storage_desc_params(self):
224         params = self._parse_nested_params(self._storage_param_cleaners)
225         self.storage['Desc Params'] = params
226 
227     _storage_param_cleaners = {}
228 
229     def _parse_nested_params(self, cleaners):
230         params = {}
231         while True:
232             try:
233                 tup = self._next_tuple()
234             except StopIteration:
235                 break
236             if pd.isnull(tup[1]):
237                 break
238 
239             key, value = tup[1:]
240 
241             if key.lower() in cleaners:
242                 cleaner = cleaners[key.lower()]
243                 value = cleaner(value)
244             params[key] = value
245 
246         return params
247 
248 
249 def _clean_param_name(x):
250     return x.strip().strip(':').lower()
251 
252 
253 def _get_meta(attr, key):
254     @property
255     def f(self):
256         data = getattr(self, attr)
257         if isinstance(key, list):
258             result = data
259             for k in key:
260                 if k not in result:
261                     raise KeyError(k)
262                 result = result[k]
263             return result
264         else:
265             return data[key]
266     return f
267 
268 
269 class TableMetadata(object):
270 
271     """
272     Container for the parsed and wrangled results of DESCRIBE FORMATTED for
273     easier Ibis use (and testing).
274     """
275     def __init__(self, schema, info, storage, partitions=None):
276         self.schema = schema
277         self.info = info
278         self.storage = storage
279         self.partitions = partitions
280 
281     def __repr__(self):
282         import pprint
283 
284         # Quick and dirty for now
285         buf = StringIO()
286         buf.write(str(type(self)))
287         buf.write('\n')
288 
289         data = {
290             'schema': self.schema,
291             'info': self.info,
292             'storage info': self.storage
293         }
294         if self.partitions is not None:
295             data['partition schema'] = self.partitions
296 
297         pprint.pprint(data, stream=buf)
298 
299         return buf.getvalue()
300 
301     @property
302     def is_partitioned(self):
303         return self.partitions is not None
304 
305     create_time = _get_meta('info', 'CreateTime')
306     location = _get_meta('info', 'Location')
307     owner = _get_meta('info', 'Owner')
308     num_rows = _get_meta('info', ['Table Parameters', 'numRows'])
309     hive_format = _get_meta('storage', 'InputFormat')
310 
311     tbl_properties = _get_meta('info', 'Table Parameters')
312     serde_properties = _get_meta('storage', 'Desc Params')
313 
314 
315 class TableInfo(object):
316     pass
317 
318 
319 class TableStorageInfo(object):
320     pass
321
```
--- 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/ibis/impala/metadata.py b/ibis/impala/metadata.py
--- a/ibis/impala/metadata.py
+++ b/ibis/impala/metadata.py
@@ -43,14 +43,16 @@
 
 def _try_timestamp(x):
     try:
-        return pd.Timestamp(x)
+        ts = pd.Timestamp(x)
+        return ts.to_pydatetime()
     except (ValueError, TypeError):
         return x
 
 
 def _try_unix_timestamp(x):
     try:
-        return pd.Timestamp.fromtimestamp(int(x))
+        ts = pd.Timestamp.fromtimestamp(int(x))
+        return ts.to_pydatetime()
     except (ValueError, TypeError):
         return x

{"golden_diff": "diff --git a/ibis/impala/metadata.py b/ibis/impala/metadata.py\n--- a/ibis/impala/metadata.py\n+++ b/ibis/impala/metadata.py\n@@ -43,14 +43,16 @@\n \n def _try_timestamp(x):\n     try:\n-        return pd.Timestamp(x)\n+        ts = pd.Timestamp(x)\n+        return ts.to_pydatetime()\n     except (ValueError, TypeError):\n         return x\n \n \n def _try_unix_timestamp(x):\n     try:\n-        return pd.Timestamp.fromtimestamp(int(x))\n+        ts = pd.Timestamp.fromtimestamp(int(x))\n+        return ts.to_pydatetime()\n     except (ValueError, TypeError):\n         return x\n", "issue": "ImpalaTable.describe_formatted may throw an exception\nexcerpt. Will investigate soon\n\n``` python\n---------------------------------------------------------------------------\nTypeError                                 Traceback (most recent call last)\n<ipython-input-115-87d40e40503e> in <module>()\n----> 1 repr(db.csv_as_parquet.describe_formatted())\n\n/home/wesm/code/cloudera/ibis/ibis/impala/metadata.py in __repr__(self)\n    295             data['partition schema'] = self.partitions\n    296 \n--> 297         pprint.pprint(data, stream=buf)\n    298 \n    299         return buf.getvalue()\n\n/home/wesm/anaconda3/lib/python3.5/pprint.py in pprint(object, stream, indent, width, depth, compact)\n     51         stream=stream, indent=indent, width=width, depth=depth,\n     52         compact=compact)\n---> 53     printer.pprint(object)\n     54 \n     55 def pformat(object, indent=1, width=80, depth=None, *, compact=False):\n```\n\n", "before_files": [{"content": "# Copyright 2014 Cloudera Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# 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 six import StringIO\nimport pandas as pd\n\n\ndef parse_metadata(descr_table):\n    parser = MetadataParser(descr_table)\n    return parser.parse()\n\n\ndef _noop(tup):\n    return None\n\n\ndef _item_converter(i):\n    def _get_item(converter=None):\n        def _converter(tup):\n            result = tup[i]\n            if converter is not None:\n                result = converter(result)\n            return result\n\n        return _converter\n\n    return _get_item\n\n_get_type = _item_converter(1)\n_get_comment = _item_converter(2)\n\n\ndef _try_timestamp(x):\n    try:\n        return pd.Timestamp(x)\n    except (ValueError, TypeError):\n        return x\n\n\ndef _try_unix_timestamp(x):\n    try:\n        return pd.Timestamp.fromtimestamp(int(x))\n    except (ValueError, TypeError):\n        return x\n\n\ndef _try_boolean(x):\n    try:\n        x = x.lower()\n        if x in ('true', 'yes'):\n            return True\n        elif x in ('false', 'no'):\n            return False\n        return x\n    except (ValueError, TypeError):\n        return x\n\n\ndef _try_int(x):\n    try:\n        return int(x)\n    except (ValueError, TypeError):\n        return x\n\n\nclass MetadataParser(object):\n\n    \"\"\"\n    A simple state-ish machine to parse the results of DESCRIBE FORMATTED\n    \"\"\"\n\n    def __init__(self, table):\n        self.table = table\n        self.tuples = list(self.table.itertuples(index=False))\n\n    def _reset(self):\n        self.pos = 0\n        self.schema = None\n        self.partitions = None\n        self.info = None\n        self.storage = None\n\n    def _next_tuple(self):\n        if self.pos == len(self.tuples):\n            raise StopIteration\n\n        result = self.tuples[self.pos]\n        self.pos += 1\n        return result\n\n    def parse(self):\n        self._reset()\n        self._parse()\n\n        return TableMetadata(self.schema, self.info, self.storage,\n                             partitions=self.partitions)\n\n    def _parse(self):\n        self.schema = self._parse_schema()\n\n        next_section = self._next_tuple()\n        if 'partition' in next_section[0].lower():\n            self._parse_partitions()\n        else:\n            self._parse_info()\n\n    def _parse_partitions(self):\n        self.partitions = self._parse_schema()\n\n        next_section = self._next_tuple()\n        if 'table information' not in next_section[0].lower():\n            raise ValueError('Table information not present')\n\n        self._parse_info()\n\n    def _parse_schema(self):\n        tup = self._next_tuple()\n        if 'col_name' not in tup[0]:\n            raise ValueError('DESCRIBE FORMATTED did not return '\n                             'the expected results: {0}'\n                             .format(tup))\n        self._next_tuple()\n\n        # Use for both main schema and partition schema (if any)\n        schema = []\n        while True:\n            tup = self._next_tuple()\n            if tup[0].strip() == '':\n                break\n            schema.append((tup[0], tup[1]))\n\n        return schema\n\n    def _parse_info(self):\n        self.info = {}\n        while True:\n            tup = self._next_tuple()\n            orig_key = tup[0].strip(':')\n            key = _clean_param_name(tup[0])\n\n            if key == '' or key.startswith('#'):\n                # section is done\n                break\n\n            if key == 'table parameters':\n                self._parse_table_parameters()\n            elif key in self._info_cleaners:\n                result = self._info_cleaners[key](tup)\n                self.info[orig_key] = result\n            else:\n                self.info[orig_key] = tup[1]\n\n        if 'storage information' not in key:\n            raise ValueError('Storage information not present')\n\n        self._parse_storage_info()\n\n    _info_cleaners = {\n        'database': _get_type(),\n        'owner': _get_type(),\n        'createtime': _get_type(_try_timestamp),\n        'lastaccesstime': _get_type(_try_timestamp),\n        'protect mode': _get_type(),\n        'retention': _get_type(_try_int),\n        'location': _get_type(),\n        'table type': _get_type()\n    }\n\n    def _parse_table_parameters(self):\n        params = self._parse_nested_params(self._table_param_cleaners)\n        self.info['Table Parameters'] = params\n\n    _table_param_cleaners = {\n        'external': _try_boolean,\n        'column_stats_accurate': _try_boolean,\n        'numfiles': _try_int,\n        'totalsize': _try_int,\n        'stats_generated_via_stats_task': _try_boolean,\n        'numrows': _try_int,\n        'transient_lastddltime': _try_unix_timestamp,\n    }\n\n    def _parse_storage_info(self):\n        self.storage = {}\n        while True:\n            # end of the road\n            try:\n                tup = self._next_tuple()\n            except StopIteration:\n                break\n\n            orig_key = tup[0].strip(':')\n            key = _clean_param_name(tup[0])\n\n            if key == '' or key.startswith('#'):\n                # section is done\n                break\n\n            if key == 'storage desc params':\n                self._parse_storage_desc_params()\n            elif key in self._storage_cleaners:\n                result = self._storage_cleaners[key](tup)\n                self.storage[orig_key] = result\n            else:\n                self.storage[orig_key] = tup[1]\n\n    _storage_cleaners = {\n        'compressed': _get_type(_try_boolean),\n        'num buckets': _get_type(_try_int),\n    }\n\n    def _parse_storage_desc_params(self):\n        params = self._parse_nested_params(self._storage_param_cleaners)\n        self.storage['Desc Params'] = params\n\n    _storage_param_cleaners = {}\n\n    def _parse_nested_params(self, cleaners):\n        params = {}\n        while True:\n            try:\n                tup = self._next_tuple()\n            except StopIteration:\n                break\n            if pd.isnull(tup[1]):\n                break\n\n            key, value = tup[1:]\n\n            if key.lower() in cleaners:\n                cleaner = cleaners[key.lower()]\n                value = cleaner(value)\n            params[key] = value\n\n        return params\n\n\ndef _clean_param_name(x):\n    return x.strip().strip(':').lower()\n\n\ndef _get_meta(attr, key):\n    @property\n    def f(self):\n        data = getattr(self, attr)\n        if isinstance(key, list):\n            result = data\n            for k in key:\n                if k not in result:\n                    raise KeyError(k)\n                result = result[k]\n            return result\n        else:\n            return data[key]\n    return f\n\n\nclass TableMetadata(object):\n\n    \"\"\"\n    Container for the parsed and wrangled results of DESCRIBE FORMATTED for\n    easier Ibis use (and testing).\n    \"\"\"\n    def __init__(self, schema, info, storage, partitions=None):\n        self.schema = schema\n        self.info = info\n        self.storage = storage\n        self.partitions = partitions\n\n    def __repr__(self):\n        import pprint\n\n        # Quick and dirty for now\n        buf = StringIO()\n        buf.write(str(type(self)))\n        buf.write('\\n')\n\n        data = {\n            'schema': self.schema,\n            'info': self.info,\n            'storage info': self.storage\n        }\n        if self.partitions is not None:\n            data['partition schema'] = self.partitions\n\n        pprint.pprint(data, stream=buf)\n\n        return buf.getvalue()\n\n    @property\n    def is_partitioned(self):\n        return self.partitions is not None\n\n    create_time = _get_meta('info', 'CreateTime')\n    location = _get_meta('info', 'Location')\n    owner = _get_meta('info', 'Owner')\n    num_rows = _get_meta('info', ['Table Parameters', 'numRows'])\n    hive_format = _get_meta('storage', 'InputFormat')\n\n    tbl_properties = _get_meta('info', 'Table Parameters')\n    serde_properties = _get_meta('storage', 'Desc Params')\n\n\nclass TableInfo(object):\n    pass\n\n\nclass TableStorageInfo(object):\n    pass\n", "path": "ibis/impala/metadata.py"}], "after_files": [{"content": "# Copyright 2014 Cloudera Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# 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 six import StringIO\nimport pandas as pd\n\n\ndef parse_metadata(descr_table):\n    parser = MetadataParser(descr_table)\n    return parser.parse()\n\n\ndef _noop(tup):\n    return None\n\n\ndef _item_converter(i):\n    def _get_item(converter=None):\n        def _converter(tup):\n            result = tup[i]\n            if converter is not None:\n                result = converter(result)\n            return result\n\n        return _converter\n\n    return _get_item\n\n_get_type = _item_converter(1)\n_get_comment = _item_converter(2)\n\n\ndef _try_timestamp(x):\n    try:\n        ts = pd.Timestamp(x)\n        return ts.to_pydatetime()\n    except (ValueError, TypeError):\n        return x\n\n\ndef _try_unix_timestamp(x):\n    try:\n        ts = pd.Timestamp.fromtimestamp(int(x))\n        return ts.to_pydatetime()\n    except (ValueError, TypeError):\n        return x\n\n\ndef _try_boolean(x):\n    try:\n        x = x.lower()\n        if x in ('true', 'yes'):\n            return True\n        elif x in ('false', 'no'):\n            return False\n        return x\n    except (ValueError, TypeError):\n        return x\n\n\ndef _try_int(x):\n    try:\n        return int(x)\n    except (ValueError, TypeError):\n        return x\n\n\nclass MetadataParser(object):\n\n    \"\"\"\n    A simple state-ish machine to parse the results of DESCRIBE FORMATTED\n    \"\"\"\n\n    def __init__(self, table):\n        self.table = table\n        self.tuples = list(self.table.itertuples(index=False))\n\n    def _reset(self):\n        self.pos = 0\n        self.schema = None\n        self.partitions = None\n        self.info = None\n        self.storage = None\n\n    def _next_tuple(self):\n        if self.pos == len(self.tuples):\n            raise StopIteration\n\n        result = self.tuples[self.pos]\n        self.pos += 1\n        return result\n\n    def parse(self):\n        self._reset()\n        self._parse()\n\n        return TableMetadata(self.schema, self.info, self.storage,\n                             partitions=self.partitions)\n\n    def _parse(self):\n        self.schema = self._parse_schema()\n\n        next_section = self._next_tuple()\n        if 'partition' in next_section[0].lower():\n            self._parse_partitions()\n        else:\n            self._parse_info()\n\n    def _parse_partitions(self):\n        self.partitions = self._parse_schema()\n\n        next_section = self._next_tuple()\n        if 'table information' not in next_section[0].lower():\n            raise ValueError('Table information not present')\n\n        self._parse_info()\n\n    def _parse_schema(self):\n        tup = self._next_tuple()\n        if 'col_name' not in tup[0]:\n            raise ValueError('DESCRIBE FORMATTED did not return '\n                             'the expected results: {0}'\n                             .format(tup))\n        self._next_tuple()\n\n        # Use for both main schema and partition schema (if any)\n        schema = []\n        while True:\n            tup = self._next_tuple()\n            if tup[0].strip() == '':\n                break\n            schema.append((tup[0], tup[1]))\n\n        return schema\n\n    def _parse_info(self):\n        self.info = {}\n        while True:\n            tup = self._next_tuple()\n            orig_key = tup[0].strip(':')\n            key = _clean_param_name(tup[0])\n\n            if key == '' or key.startswith('#'):\n                # section is done\n                break\n\n            if key == 'table parameters':\n                self._parse_table_parameters()\n            elif key in self._info_cleaners:\n                result = self._info_cleaners[key](tup)\n                self.info[orig_key] = result\n            else:\n                self.info[orig_key] = tup[1]\n\n        if 'storage information' not in key:\n            raise ValueError('Storage information not present')\n\n        self._parse_storage_info()\n\n    _info_cleaners = {\n        'database': _get_type(),\n        'owner': _get_type(),\n        'createtime': _get_type(_try_timestamp),\n        'lastaccesstime': _get_type(_try_timestamp),\n        'protect mode': _get_type(),\n        'retention': _get_type(_try_int),\n        'location': _get_type(),\n        'table type': _get_type()\n    }\n\n    def _parse_table_parameters(self):\n        params = self._parse_nested_params(self._table_param_cleaners)\n        self.info['Table Parameters'] = params\n\n    _table_param_cleaners = {\n        'external': _try_boolean,\n        'column_stats_accurate': _try_boolean,\n        'numfiles': _try_int,\n        'totalsize': _try_int,\n        'stats_generated_via_stats_task': _try_boolean,\n        'numrows': _try_int,\n        'transient_lastddltime': _try_unix_timestamp,\n    }\n\n    def _parse_storage_info(self):\n        self.storage = {}\n        while True:\n            # end of the road\n            try:\n                tup = self._next_tuple()\n            except StopIteration:\n                break\n\n            orig_key = tup[0].strip(':')\n            key = _clean_param_name(tup[0])\n\n            if key == '' or key.startswith('#'):\n                # section is done\n                break\n\n            if key == 'storage desc params':\n                self._parse_storage_desc_params()\n            elif key in self._storage_cleaners:\n                result = self._storage_cleaners[key](tup)\n                self.storage[orig_key] = result\n            else:\n                self.storage[orig_key] = tup[1]\n\n    _storage_cleaners = {\n        'compressed': _get_type(_try_boolean),\n        'num buckets': _get_type(_try_int),\n    }\n\n    def _parse_storage_desc_params(self):\n        params = self._parse_nested_params(self._storage_param_cleaners)\n        self.storage['Desc Params'] = params\n\n    _storage_param_cleaners = {}\n\n    def _parse_nested_params(self, cleaners):\n        params = {}\n        while True:\n            try:\n                tup = self._next_tuple()\n            except StopIteration:\n                break\n            if pd.isnull(tup[1]):\n                break\n\n            key, value = tup[1:]\n\n            if key.lower() in cleaners:\n                cleaner = cleaners[key.lower()]\n                value = cleaner(value)\n            params[key] = value\n\n        return params\n\n\ndef _clean_param_name(x):\n    return x.strip().strip(':').lower()\n\n\ndef _get_meta(attr, key):\n    @property\n    def f(self):\n        data = getattr(self, attr)\n        if isinstance(key, list):\n            result = data\n            for k in key:\n                if k not in result:\n                    raise KeyError(k)\n                result = result[k]\n            return result\n        else:\n            return data[key]\n    return f\n\n\nclass TableMetadata(object):\n\n    \"\"\"\n    Container for the parsed and wrangled results of DESCRIBE FORMATTED for\n    easier Ibis use (and testing).\n    \"\"\"\n    def __init__(self, schema, info, storage, partitions=None):\n        self.schema = schema\n        self.info = info\n        self.storage = storage\n        self.partitions = partitions\n\n    def __repr__(self):\n        import pprint\n\n        # Quick and dirty for now\n        buf = StringIO()\n        buf.write(str(type(self)))\n        buf.write('\\n')\n\n        data = {\n            'schema': self.schema,\n            'info': self.info,\n            'storage info': self.storage\n        }\n        if self.partitions is not None:\n            data['partition schema'] = self.partitions\n\n        pprint.pprint(data, stream=buf)\n\n        return buf.getvalue()\n\n    @property\n    def is_partitioned(self):\n        return self.partitions is not None\n\n    create_time = _get_meta('info', 'CreateTime')\n    location = _get_meta('info', 'Location')\n    owner = _get_meta('info', 'Owner')\n    num_rows = _get_meta('info', ['Table Parameters', 'numRows'])\n    hive_format = _get_meta('storage', 'InputFormat')\n\n    tbl_properties = _get_meta('info', 'Table Parameters')\n    serde_properties = _get_meta('storage', 'Desc Params')\n\n\nclass TableInfo(object):\n    pass\n\n\nclass TableStorageInfo(object):\n    pass\n", "path": "ibis/impala/metadata.py"}]}