We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Catalog getitem fails with Numpy int index
I just tried to get a catalog source by int index, and my int was a numpy int.

This failed with a ValueError due to this check:
https://github.com/gammapy/gammapy/blob/c77fe799202acdc880db717ffd85106c1d46f326/gammapy/catalog/core.py#L169

To check that this doesn't work as expected:
```python
>>> import six
>>> import numpy as np
>>> isinstance(np.int,  six.integer_types)
False
```

I guess we should find some better incantation to check for int via Google.
(and add a regression test, showing that it works for numpy int)

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gammapy/utils/array.py`
Content:
```
1 # Licensed under a 3-clause BSD style license - see LICENSE.rst
2 """Utility functions to deal with arrays and quantities.
3 """
4 from __future__ import absolute_import, division, print_function, unicode_literals
5 import numpy as np
6 
7 __all__ = ['array_stats_str',
8            'shape_2N',
9            'shape_divisible_by',
10            'symmetric_crop_pad_width']
11 
12 
13 def array_stats_str(x, label=''):
14     """Make a string summarising some stats for an array.
15 
16     Parameters
17     ----------
18     x : array-like
19         Array
20     label : str, optional
21         Label
22 
23     Returns
24     -------
25     stats_str : str
26         String with array stats
27     """
28     x = np.asanyarray(x)
29 
30     ss = ''
31     if label:
32         ss += '{0:15s}: '.format(label)
33 
34     min = x.min()
35     max = x.max()
36     size = x.size
37 
38     fmt = 'size = {size:5d}, min = {min:6.3f}, max = {max:6.3f}\n'
39     ss += fmt.format(**locals())
40 
41     return ss
42 
43 
44 def shape_2N(shape, N=3):
45     """
46     Round a given shape to values that are divisible by 2^N.
47 
48     Parameters
49     ----------
50     shape : tuple
51         Input shape.
52     N : int (default = 3), optional
53         Exponent of two.
54 
55     Returns
56     -------
57     new_shape : Tuple
58         New shape extended to integers divisible by 2^N
59     """
60     shape = np.array(shape)
61     new_shape = shape + (2 ** N - np.mod(shape, 2 ** N))
62     return tuple(new_shape)
63 
64 
65 def shape_divisible_by(shape, factor):
66     """
67     Round a given shape to values that are divisible by factor.
68 
69     Parameters
70     ----------
71     shape : tuple
72         Input shape.
73     factor : int
74         Divisor.
75 
76     Returns
77     -------
78     new_shape : Tuple
79         New shape extended to integers divisible by factor
80     """
81     shape = np.array(shape)
82     new_shape = shape + (shape % factor)
83     return tuple(new_shape)
84 
85 
86 def symmetric_crop_pad_width(shape, new_shape):
87     """
88     Compute symmetric crop or pad width to obtain a new shape from a given old
89     shape of an array.
90 
91     Parameters
92     ----------
93     shape : tuple
94         Old shape
95     new_shape : tuple or str
96         New shape.
97 
98     """
99     xdiff = abs(shape[1] - new_shape[1])
100     ydiff = abs(shape[0] - new_shape[0])
101 
102     if (np.array([xdiff, ydiff]) % 2).any():
103         raise ValueError('For symmetric crop / pad width, difference to new shape '
104                          'must be even in all axes.')
105 
106     ywidth = (ydiff // 2, ydiff // 2)
107     xwidth = (xdiff // 2, xdiff // 2)
108     return (ywidth, xwidth)
```

Path: `gammapy/catalog/core.py`
Content:
```
1 # Licensed under a 3-clause BSD style license - see LICENSE.rst
2 """Source catalog and object base classes.
3 """
4 from __future__ import absolute_import, division, print_function, unicode_literals
5 from collections import OrderedDict
6 import sys
7 from pprint import pprint
8 from astropy.extern import six
9 from astropy.utils import lazyproperty
10 from astropy.units import Quantity
11 
12 __all__ = [
13     'SourceCatalog',
14     'SourceCatalogObject',
15 ]
16 
17 
18 class SourceCatalogObject(object):
19     """Source catalog object.
20 
21     This class can be used directly, but it's mostly used as a
22     base class for the other source catalog classes.
23 
24     The catalog data on this source is stored in the `source.data`
25     attribute as on OrderedDict.
26 
27     The source catalog object is decoupled from the source catalog,
28     it doesn't hold a reference back to it.
29     The catalog table row index is stored in `_table_row_index` though,
30     because it can be useful for debugging or display.
31     """
32     _source_name_key = 'Source_Name'
33     _source_index_key = 'catalog_row_index'
34 
35     def __init__(self, data):
36         self.data = data
37 
38     @property
39     def name(self):
40         """Source name"""
41         name = self.data[self._source_name_key]
42         return name.strip()
43 
44     @property
45     def index(self):
46         """Row index of source in catalog"""
47         return self.data[self._source_index_key]
48 
49     def pprint(self, file=None):
50         """Pretty-print source data"""
51         if not file:
52             file = sys.stdout
53 
54         pprint(self.data, stream=file)
55 
56         # TODO: add methods to serialise to JSON and YAML
57         # and also to quickly pretty-print output in that format for interactive use.
58         # Maybe even add HTML output for IPython repr?
59         # Or at to_table method?
60 
61     def info(self):
62         """
63         Print summary info about the object.
64         """
65         print(self)
66 
67 
68 class SourceCatalog(object):
69     """Generic source catalog.
70 
71     This class can be used directly, but it's mostly used as a
72     base class for the other source catalog classes.
73 
74     This is a thin wrapper around `~astropy.table.Table`,
75     which is stored in the `catalog.table` attribute.
76 
77     Parameters
78     ----------
79     table : `~astropy.table.Table`
80         Table with catalog data.
81     source_name_key : str ('Source_Name')
82         Column with source name information
83     source_name_alias : tuple of str
84         Columns with source name aliases. This will allow accessing the source
85         row by alias names as well.
86     """
87     source_object_class = SourceCatalogObject
88 
89     # TODO: at the moment these are duplicated in SourceCatalogObject.
90     # Should we share them somehow?
91     _source_index_key = 'catalog_row_index'
92 
93     def __init__(self, table, source_name_key='Source_Name', source_name_alias=()):
94         self.table = table
95         self._source_name_key = source_name_key
96         self._source_name_alias = source_name_alias
97 
98     @lazyproperty
99     def _name_to_index_cache(self):
100         # Make a dict for quick lookup: source name -> row index
101         names = dict()
102         for idx, row in enumerate(self.table):
103             name = row[self._source_name_key]
104             names[name.strip()] = idx
105             for alias_column in self._source_name_alias:
106                 for alias in row[alias_column].split(','):
107                     if not alias == '':
108                         names[alias.strip()] = idx
109         return names
110 
111     def row_index(self, name):
112         """Look up row index of source by name.
113 
114         Parameters
115         ----------
116         name : str
117             Source name
118 
119         Returns
120         -------
121         index : int
122             Row index of source in table
123         """
124         index = self._name_to_index_cache[name]
125         row = self.table[index]
126         # check if name lookup is correct other wise recompute _name_to_index_cache
127 
128         possible_names = [row[self._source_name_key]]
129         for alias_column in self._source_name_alias:
130             possible_names += row[alias_column].split(',')
131 
132         if not name in possible_names:
133             self.__dict__.pop('_name_to_index_cache')
134             index = self._name_to_index_cache[name]
135         return index
136 
137     def source_name(self, index):
138         """Look up source name by row index.
139 
140         Parameters
141         ----------
142         index : int
143             Row index of source in table
144         """
145         source_name_col = self.table[self._source_name_key]
146         name = source_name_col[index]
147         return name.strip()
148 
149     def __getitem__(self, key):
150         """Get source by name.
151 
152         Parameters
153         ----------
154         key : str or int
155             Source name or row index
156 
157         Returns
158         -------
159         source : `SourceCatalogObject`
160             An object representing one source.
161 
162         Notes
163         -----
164         At the moment this can raise KeyError, IndexError and ValueError
165         for invalid keys. Should we always raise KeyError to simplify this?
166         """
167         if isinstance(key, six.string_types):
168             index = self.row_index(key)
169         elif isinstance(key, six.integer_types):
170             index = key
171         else:
172             msg = 'Key must be source name string or row index integer. '
173             msg += 'Type not understood: {}'.format(type(key))
174             raise ValueError(msg)
175 
176         return self._make_source_object(index)
177 
178     def _make_source_object(self, index):
179         """Make one source object.
180 
181         Parameters
182         ----------
183         index : int
184             Row index
185 
186         Returns
187         -------
188         source : `SourceCatalogObject`
189             Source object
190         """
191         data = self._make_source_dict(index)
192         source = self.source_object_class(data)
193         return source
194 
195     def _make_source_dict(self, idx):
196         """Make one source data dict.
197 
198         Parameters
199         ----------
200         idx : int
201             Row index
202 
203         Returns
204         -------
205         data : `~collections.OrderedDict`
206             Source data
207         """
208 
209         data = OrderedDict()
210         for colname in self.table.colnames:
211             col = self.table[colname]
212 
213             if isinstance(col, Quantity):
214                 val = col[idx]
215             else:
216                 val = col.data[idx]
217                 if col.unit:
218                     val = Quantity(val, col.unit)
219 
220             data[colname] = val
221 
222         data[self._source_index_key] = idx
223         return data
224 
225     def info(self):
226         """
227         Print info string.
228         """
229         print(self)
230 
231     def __str__(self):
232         """
233         Info string
234         """
235         ss = self.description
236         ss += ' with {} objects.'.format(len(self.table))
237         return ss
```
--- 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/gammapy/catalog/core.py b/gammapy/catalog/core.py
--- a/gammapy/catalog/core.py
+++ b/gammapy/catalog/core.py
@@ -2,6 +2,7 @@
 """Source catalog and object base classes.
 """
 from __future__ import absolute_import, division, print_function, unicode_literals
+from ..utils.array import _is_int
 from collections import OrderedDict
 import sys
 from pprint import pprint
@@ -166,7 +167,7 @@
         """
         if isinstance(key, six.string_types):
             index = self.row_index(key)
-        elif isinstance(key, six.integer_types):
+        elif _is_int(key):
             index = key
         else:
             msg = 'Key must be source name string or row index integer. '
@@ -234,4 +235,4 @@
         """
         ss = self.description
         ss += ' with {} objects.'.format(len(self.table))
-        return ss
\ No newline at end of file
+        return ss
diff --git a/gammapy/utils/array.py b/gammapy/utils/array.py
--- a/gammapy/utils/array.py
+++ b/gammapy/utils/array.py
@@ -2,6 +2,7 @@
 """Utility functions to deal with arrays and quantities.
 """
 from __future__ import absolute_import, division, print_function, unicode_literals
+from astropy.extern import six
 import numpy as np
 
 __all__ = ['array_stats_str',
@@ -9,6 +10,7 @@
            'shape_divisible_by',
            'symmetric_crop_pad_width']
 
+all_integer_types = six.integer_types + (np.integer,)
 
 def array_stats_str(x, label=''):
     """Make a string summarising some stats for an array.
@@ -105,4 +107,8 @@
 
     ywidth = (ydiff // 2, ydiff // 2)
     xwidth = (xdiff // 2, xdiff // 2)
-    return (ywidth, xwidth)
\ No newline at end of file
+    return (ywidth, xwidth)
+
+
+def _is_int(val):
+    return isinstance(val, all_integer_types)

{"golden_diff": "diff --git a/gammapy/catalog/core.py b/gammapy/catalog/core.py\n--- a/gammapy/catalog/core.py\n+++ b/gammapy/catalog/core.py\n@@ -2,6 +2,7 @@\n \"\"\"Source catalog and object base classes.\n \"\"\"\n from __future__ import absolute_import, division, print_function, unicode_literals\n+from ..utils.array import _is_int\n from collections import OrderedDict\n import sys\n from pprint import pprint\n@@ -166,7 +167,7 @@\n         \"\"\"\n         if isinstance(key, six.string_types):\n             index = self.row_index(key)\n-        elif isinstance(key, six.integer_types):\n+        elif _is_int(key):\n             index = key\n         else:\n             msg = 'Key must be source name string or row index integer. '\n@@ -234,4 +235,4 @@\n         \"\"\"\n         ss = self.description\n         ss += ' with {} objects.'.format(len(self.table))\n-        return ss\n\\ No newline at end of file\n+        return ss\ndiff --git a/gammapy/utils/array.py b/gammapy/utils/array.py\n--- a/gammapy/utils/array.py\n+++ b/gammapy/utils/array.py\n@@ -2,6 +2,7 @@\n \"\"\"Utility functions to deal with arrays and quantities.\n \"\"\"\n from __future__ import absolute_import, division, print_function, unicode_literals\n+from astropy.extern import six\n import numpy as np\n \n __all__ = ['array_stats_str',\n@@ -9,6 +10,7 @@\n            'shape_divisible_by',\n            'symmetric_crop_pad_width']\n \n+all_integer_types = six.integer_types + (np.integer,)\n \n def array_stats_str(x, label=''):\n     \"\"\"Make a string summarising some stats for an array.\n@@ -105,4 +107,8 @@\n \n     ywidth = (ydiff // 2, ydiff // 2)\n     xwidth = (xdiff // 2, xdiff // 2)\n-    return (ywidth, xwidth)\n\\ No newline at end of file\n+    return (ywidth, xwidth)\n+\n+\n+def _is_int(val):\n+    return isinstance(val, all_integer_types)\n", "issue": "Catalog getitem fails with Numpy int index\nI just tried to get a catalog source by int index, and my int was a numpy int.\r\n\r\nThis failed with a ValueError due to this check:\r\nhttps://github.com/gammapy/gammapy/blob/c77fe799202acdc880db717ffd85106c1d46f326/gammapy/catalog/core.py#L169\r\n\r\nTo check that this doesn't work as expected:\r\n```python\r\n>>> import six\r\n>>> import numpy as np\r\n>>> isinstance(np.int,  six.integer_types)\r\nFalse\r\n```\r\n\r\nI guess we should find some better incantation to check for int via Google.\r\n(and add a regression test, showing that it works for numpy int)\n", "before_files": [{"content": "# Licensed under a 3-clause BSD style license - see LICENSE.rst\n\"\"\"Utility functions to deal with arrays and quantities.\n\"\"\"\nfrom __future__ import absolute_import, division, print_function, unicode_literals\nimport numpy as np\n\n__all__ = ['array_stats_str',\n           'shape_2N',\n           'shape_divisible_by',\n           'symmetric_crop_pad_width']\n\n\ndef array_stats_str(x, label=''):\n    \"\"\"Make a string summarising some stats for an array.\n\n    Parameters\n    ----------\n    x : array-like\n        Array\n    label : str, optional\n        Label\n\n    Returns\n    -------\n    stats_str : str\n        String with array stats\n    \"\"\"\n    x = np.asanyarray(x)\n\n    ss = ''\n    if label:\n        ss += '{0:15s}: '.format(label)\n\n    min = x.min()\n    max = x.max()\n    size = x.size\n\n    fmt = 'size = {size:5d}, min = {min:6.3f}, max = {max:6.3f}\\n'\n    ss += fmt.format(**locals())\n\n    return ss\n\n\ndef shape_2N(shape, N=3):\n    \"\"\"\n    Round a given shape to values that are divisible by 2^N.\n\n    Parameters\n    ----------\n    shape : tuple\n        Input shape.\n    N : int (default = 3), optional\n        Exponent of two.\n\n    Returns\n    -------\n    new_shape : Tuple\n        New shape extended to integers divisible by 2^N\n    \"\"\"\n    shape = np.array(shape)\n    new_shape = shape + (2 ** N - np.mod(shape, 2 ** N))\n    return tuple(new_shape)\n\n\ndef shape_divisible_by(shape, factor):\n    \"\"\"\n    Round a given shape to values that are divisible by factor.\n\n    Parameters\n    ----------\n    shape : tuple\n        Input shape.\n    factor : int\n        Divisor.\n\n    Returns\n    -------\n    new_shape : Tuple\n        New shape extended to integers divisible by factor\n    \"\"\"\n    shape = np.array(shape)\n    new_shape = shape + (shape % factor)\n    return tuple(new_shape)\n\n\ndef symmetric_crop_pad_width(shape, new_shape):\n    \"\"\"\n    Compute symmetric crop or pad width to obtain a new shape from a given old\n    shape of an array.\n\n    Parameters\n    ----------\n    shape : tuple\n        Old shape\n    new_shape : tuple or str\n        New shape.\n\n    \"\"\"\n    xdiff = abs(shape[1] - new_shape[1])\n    ydiff = abs(shape[0] - new_shape[0])\n\n    if (np.array([xdiff, ydiff]) % 2).any():\n        raise ValueError('For symmetric crop / pad width, difference to new shape '\n                         'must be even in all axes.')\n\n    ywidth = (ydiff // 2, ydiff // 2)\n    xwidth = (xdiff // 2, xdiff // 2)\n    return (ywidth, xwidth)", "path": "gammapy/utils/array.py"}, {"content": "# Licensed under a 3-clause BSD style license - see LICENSE.rst\n\"\"\"Source catalog and object base classes.\n\"\"\"\nfrom __future__ import absolute_import, division, print_function, unicode_literals\nfrom collections import OrderedDict\nimport sys\nfrom pprint import pprint\nfrom astropy.extern import six\nfrom astropy.utils import lazyproperty\nfrom astropy.units import Quantity\n\n__all__ = [\n    'SourceCatalog',\n    'SourceCatalogObject',\n]\n\n\nclass SourceCatalogObject(object):\n    \"\"\"Source catalog object.\n\n    This class can be used directly, but it's mostly used as a\n    base class for the other source catalog classes.\n\n    The catalog data on this source is stored in the `source.data`\n    attribute as on OrderedDict.\n\n    The source catalog object is decoupled from the source catalog,\n    it doesn't hold a reference back to it.\n    The catalog table row index is stored in `_table_row_index` though,\n    because it can be useful for debugging or display.\n    \"\"\"\n    _source_name_key = 'Source_Name'\n    _source_index_key = 'catalog_row_index'\n\n    def __init__(self, data):\n        self.data = data\n\n    @property\n    def name(self):\n        \"\"\"Source name\"\"\"\n        name = self.data[self._source_name_key]\n        return name.strip()\n\n    @property\n    def index(self):\n        \"\"\"Row index of source in catalog\"\"\"\n        return self.data[self._source_index_key]\n\n    def pprint(self, file=None):\n        \"\"\"Pretty-print source data\"\"\"\n        if not file:\n            file = sys.stdout\n\n        pprint(self.data, stream=file)\n\n        # TODO: add methods to serialise to JSON and YAML\n        # and also to quickly pretty-print output in that format for interactive use.\n        # Maybe even add HTML output for IPython repr?\n        # Or at to_table method?\n\n    def info(self):\n        \"\"\"\n        Print summary info about the object.\n        \"\"\"\n        print(self)\n\n\nclass SourceCatalog(object):\n    \"\"\"Generic source catalog.\n\n    This class can be used directly, but it's mostly used as a\n    base class for the other source catalog classes.\n\n    This is a thin wrapper around `~astropy.table.Table`,\n    which is stored in the `catalog.table` attribute.\n\n    Parameters\n    ----------\n    table : `~astropy.table.Table`\n        Table with catalog data.\n    source_name_key : str ('Source_Name')\n        Column with source name information\n    source_name_alias : tuple of str\n        Columns with source name aliases. This will allow accessing the source\n        row by alias names as well.\n    \"\"\"\n    source_object_class = SourceCatalogObject\n\n    # TODO: at the moment these are duplicated in SourceCatalogObject.\n    # Should we share them somehow?\n    _source_index_key = 'catalog_row_index'\n\n    def __init__(self, table, source_name_key='Source_Name', source_name_alias=()):\n        self.table = table\n        self._source_name_key = source_name_key\n        self._source_name_alias = source_name_alias\n\n    @lazyproperty\n    def _name_to_index_cache(self):\n        # Make a dict for quick lookup: source name -> row index\n        names = dict()\n        for idx, row in enumerate(self.table):\n            name = row[self._source_name_key]\n            names[name.strip()] = idx\n            for alias_column in self._source_name_alias:\n                for alias in row[alias_column].split(','):\n                    if not alias == '':\n                        names[alias.strip()] = idx\n        return names\n\n    def row_index(self, name):\n        \"\"\"Look up row index of source by name.\n\n        Parameters\n        ----------\n        name : str\n            Source name\n\n        Returns\n        -------\n        index : int\n            Row index of source in table\n        \"\"\"\n        index = self._name_to_index_cache[name]\n        row = self.table[index]\n        # check if name lookup is correct other wise recompute _name_to_index_cache\n\n        possible_names = [row[self._source_name_key]]\n        for alias_column in self._source_name_alias:\n            possible_names += row[alias_column].split(',')\n\n        if not name in possible_names:\n            self.__dict__.pop('_name_to_index_cache')\n            index = self._name_to_index_cache[name]\n        return index\n\n    def source_name(self, index):\n        \"\"\"Look up source name by row index.\n\n        Parameters\n        ----------\n        index : int\n            Row index of source in table\n        \"\"\"\n        source_name_col = self.table[self._source_name_key]\n        name = source_name_col[index]\n        return name.strip()\n\n    def __getitem__(self, key):\n        \"\"\"Get source by name.\n\n        Parameters\n        ----------\n        key : str or int\n            Source name or row index\n\n        Returns\n        -------\n        source : `SourceCatalogObject`\n            An object representing one source.\n\n        Notes\n        -----\n        At the moment this can raise KeyError, IndexError and ValueError\n        for invalid keys. Should we always raise KeyError to simplify this?\n        \"\"\"\n        if isinstance(key, six.string_types):\n            index = self.row_index(key)\n        elif isinstance(key, six.integer_types):\n            index = key\n        else:\n            msg = 'Key must be source name string or row index integer. '\n            msg += 'Type not understood: {}'.format(type(key))\n            raise ValueError(msg)\n\n        return self._make_source_object(index)\n\n    def _make_source_object(self, index):\n        \"\"\"Make one source object.\n\n        Parameters\n        ----------\n        index : int\n            Row index\n\n        Returns\n        -------\n        source : `SourceCatalogObject`\n            Source object\n        \"\"\"\n        data = self._make_source_dict(index)\n        source = self.source_object_class(data)\n        return source\n\n    def _make_source_dict(self, idx):\n        \"\"\"Make one source data dict.\n\n        Parameters\n        ----------\n        idx : int\n            Row index\n\n        Returns\n        -------\n        data : `~collections.OrderedDict`\n            Source data\n        \"\"\"\n\n        data = OrderedDict()\n        for colname in self.table.colnames:\n            col = self.table[colname]\n\n            if isinstance(col, Quantity):\n                val = col[idx]\n            else:\n                val = col.data[idx]\n                if col.unit:\n                    val = Quantity(val, col.unit)\n\n            data[colname] = val\n\n        data[self._source_index_key] = idx\n        return data\n\n    def info(self):\n        \"\"\"\n        Print info string.\n        \"\"\"\n        print(self)\n\n    def __str__(self):\n        \"\"\"\n        Info string\n        \"\"\"\n        ss = self.description\n        ss += ' with {} objects.'.format(len(self.table))\n        return ss", "path": "gammapy/catalog/core.py"}], "after_files": [{"content": "# Licensed under a 3-clause BSD style license - see LICENSE.rst\n\"\"\"Utility functions to deal with arrays and quantities.\n\"\"\"\nfrom __future__ import absolute_import, division, print_function, unicode_literals\nfrom astropy.extern import six\nimport numpy as np\n\n__all__ = ['array_stats_str',\n           'shape_2N',\n           'shape_divisible_by',\n           'symmetric_crop_pad_width']\n\nall_integer_types = six.integer_types + (np.integer,)\n\ndef array_stats_str(x, label=''):\n    \"\"\"Make a string summarising some stats for an array.\n\n    Parameters\n    ----------\n    x : array-like\n        Array\n    label : str, optional\n        Label\n\n    Returns\n    -------\n    stats_str : str\n        String with array stats\n    \"\"\"\n    x = np.asanyarray(x)\n\n    ss = ''\n    if label:\n        ss += '{0:15s}: '.format(label)\n\n    min = x.min()\n    max = x.max()\n    size = x.size\n\n    fmt = 'size = {size:5d}, min = {min:6.3f}, max = {max:6.3f}\\n'\n    ss += fmt.format(**locals())\n\n    return ss\n\n\ndef shape_2N(shape, N=3):\n    \"\"\"\n    Round a given shape to values that are divisible by 2^N.\n\n    Parameters\n    ----------\n    shape : tuple\n        Input shape.\n    N : int (default = 3), optional\n        Exponent of two.\n\n    Returns\n    -------\n    new_shape : Tuple\n        New shape extended to integers divisible by 2^N\n    \"\"\"\n    shape = np.array(shape)\n    new_shape = shape + (2 ** N - np.mod(shape, 2 ** N))\n    return tuple(new_shape)\n\n\ndef shape_divisible_by(shape, factor):\n    \"\"\"\n    Round a given shape to values that are divisible by factor.\n\n    Parameters\n    ----------\n    shape : tuple\n        Input shape.\n    factor : int\n        Divisor.\n\n    Returns\n    -------\n    new_shape : Tuple\n        New shape extended to integers divisible by factor\n    \"\"\"\n    shape = np.array(shape)\n    new_shape = shape + (shape % factor)\n    return tuple(new_shape)\n\n\ndef symmetric_crop_pad_width(shape, new_shape):\n    \"\"\"\n    Compute symmetric crop or pad width to obtain a new shape from a given old\n    shape of an array.\n\n    Parameters\n    ----------\n    shape : tuple\n        Old shape\n    new_shape : tuple or str\n        New shape.\n\n    \"\"\"\n    xdiff = abs(shape[1] - new_shape[1])\n    ydiff = abs(shape[0] - new_shape[0])\n\n    if (np.array([xdiff, ydiff]) % 2).any():\n        raise ValueError('For symmetric crop / pad width, difference to new shape '\n                         'must be even in all axes.')\n\n    ywidth = (ydiff // 2, ydiff // 2)\n    xwidth = (xdiff // 2, xdiff // 2)\n    return (ywidth, xwidth)\n\n\ndef _is_int(val):\n    return isinstance(val, all_integer_types)\n", "path": "gammapy/utils/array.py"}, {"content": "# Licensed under a 3-clause BSD style license - see LICENSE.rst\n\"\"\"Source catalog and object base classes.\n\"\"\"\nfrom __future__ import absolute_import, division, print_function, unicode_literals\nfrom ..utils.array import _is_int\nfrom collections import OrderedDict\nimport sys\nfrom pprint import pprint\nfrom astropy.extern import six\nfrom astropy.utils import lazyproperty\nfrom astropy.units import Quantity\n\n__all__ = [\n    'SourceCatalog',\n    'SourceCatalogObject',\n]\n\n\nclass SourceCatalogObject(object):\n    \"\"\"Source catalog object.\n\n    This class can be used directly, but it's mostly used as a\n    base class for the other source catalog classes.\n\n    The catalog data on this source is stored in the `source.data`\n    attribute as on OrderedDict.\n\n    The source catalog object is decoupled from the source catalog,\n    it doesn't hold a reference back to it.\n    The catalog table row index is stored in `_table_row_index` though,\n    because it can be useful for debugging or display.\n    \"\"\"\n    _source_name_key = 'Source_Name'\n    _source_index_key = 'catalog_row_index'\n\n    def __init__(self, data):\n        self.data = data\n\n    @property\n    def name(self):\n        \"\"\"Source name\"\"\"\n        name = self.data[self._source_name_key]\n        return name.strip()\n\n    @property\n    def index(self):\n        \"\"\"Row index of source in catalog\"\"\"\n        return self.data[self._source_index_key]\n\n    def pprint(self, file=None):\n        \"\"\"Pretty-print source data\"\"\"\n        if not file:\n            file = sys.stdout\n\n        pprint(self.data, stream=file)\n\n        # TODO: add methods to serialise to JSON and YAML\n        # and also to quickly pretty-print output in that format for interactive use.\n        # Maybe even add HTML output for IPython repr?\n        # Or at to_table method?\n\n    def info(self):\n        \"\"\"\n        Print summary info about the object.\n        \"\"\"\n        print(self)\n\n\nclass SourceCatalog(object):\n    \"\"\"Generic source catalog.\n\n    This class can be used directly, but it's mostly used as a\n    base class for the other source catalog classes.\n\n    This is a thin wrapper around `~astropy.table.Table`,\n    which is stored in the `catalog.table` attribute.\n\n    Parameters\n    ----------\n    table : `~astropy.table.Table`\n        Table with catalog data.\n    source_name_key : str ('Source_Name')\n        Column with source name information\n    source_name_alias : tuple of str\n        Columns with source name aliases. This will allow accessing the source\n        row by alias names as well.\n    \"\"\"\n    source_object_class = SourceCatalogObject\n\n    # TODO: at the moment these are duplicated in SourceCatalogObject.\n    # Should we share them somehow?\n    _source_index_key = 'catalog_row_index'\n\n    def __init__(self, table, source_name_key='Source_Name', source_name_alias=()):\n        self.table = table\n        self._source_name_key = source_name_key\n        self._source_name_alias = source_name_alias\n\n    @lazyproperty\n    def _name_to_index_cache(self):\n        # Make a dict for quick lookup: source name -> row index\n        names = dict()\n        for idx, row in enumerate(self.table):\n            name = row[self._source_name_key]\n            names[name.strip()] = idx\n            for alias_column in self._source_name_alias:\n                for alias in row[alias_column].split(','):\n                    if not alias == '':\n                        names[alias.strip()] = idx\n        return names\n\n    def row_index(self, name):\n        \"\"\"Look up row index of source by name.\n\n        Parameters\n        ----------\n        name : str\n            Source name\n\n        Returns\n        -------\n        index : int\n            Row index of source in table\n        \"\"\"\n        index = self._name_to_index_cache[name]\n        row = self.table[index]\n        # check if name lookup is correct other wise recompute _name_to_index_cache\n\n        possible_names = [row[self._source_name_key]]\n        for alias_column in self._source_name_alias:\n            possible_names += row[alias_column].split(',')\n\n        if not name in possible_names:\n            self.__dict__.pop('_name_to_index_cache')\n            index = self._name_to_index_cache[name]\n        return index\n\n    def source_name(self, index):\n        \"\"\"Look up source name by row index.\n\n        Parameters\n        ----------\n        index : int\n            Row index of source in table\n        \"\"\"\n        source_name_col = self.table[self._source_name_key]\n        name = source_name_col[index]\n        return name.strip()\n\n    def __getitem__(self, key):\n        \"\"\"Get source by name.\n\n        Parameters\n        ----------\n        key : str or int\n            Source name or row index\n\n        Returns\n        -------\n        source : `SourceCatalogObject`\n            An object representing one source.\n\n        Notes\n        -----\n        At the moment this can raise KeyError, IndexError and ValueError\n        for invalid keys. Should we always raise KeyError to simplify this?\n        \"\"\"\n        if isinstance(key, six.string_types):\n            index = self.row_index(key)\n        elif _is_int(key):\n            index = key\n        else:\n            msg = 'Key must be source name string or row index integer. '\n            msg += 'Type not understood: {}'.format(type(key))\n            raise ValueError(msg)\n\n        return self._make_source_object(index)\n\n    def _make_source_object(self, index):\n        \"\"\"Make one source object.\n\n        Parameters\n        ----------\n        index : int\n            Row index\n\n        Returns\n        -------\n        source : `SourceCatalogObject`\n            Source object\n        \"\"\"\n        data = self._make_source_dict(index)\n        source = self.source_object_class(data)\n        return source\n\n    def _make_source_dict(self, idx):\n        \"\"\"Make one source data dict.\n\n        Parameters\n        ----------\n        idx : int\n            Row index\n\n        Returns\n        -------\n        data : `~collections.OrderedDict`\n            Source data\n        \"\"\"\n\n        data = OrderedDict()\n        for colname in self.table.colnames:\n            col = self.table[colname]\n\n            if isinstance(col, Quantity):\n                val = col[idx]\n            else:\n                val = col.data[idx]\n                if col.unit:\n                    val = Quantity(val, col.unit)\n\n            data[colname] = val\n\n        data[self._source_index_key] = idx\n        return data\n\n    def info(self):\n        \"\"\"\n        Print info string.\n        \"\"\"\n        print(self)\n\n    def __str__(self):\n        \"\"\"\n        Info string\n        \"\"\"\n        ss = self.description\n        ss += ' with {} objects.'.format(len(self.table))\n        return ss\n", "path": "gammapy/catalog/core.py"}]}