{"action":"created","comment":{"url":"https://api.github.com/repos/nipy/nibabel/pulls/comments/42656897","id":42656897,"diff_hunk":"@@ -0,0 +1,569 @@\n+# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-\n+# vi: set ft=python sts=4 ts=4 sw=4 et:\n+### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n+#\n+#   See COPYING file distributed along with the NiBabel package for the\n+#   copyright and license terms.\n+#\n+### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n+''' Simple interfaces for images'''\n+\n+try:\n+    basestring\n+except NameError:  # python 3\n+    basestring = str\n+\n+import os.path\n+import warnings\n+\n+import numpy as np\n+\n+from .fileholders import FileHolder\n+from .filename_parser import (types_filenames, TypesFilenamesError,\n+                              splitext_addext)\n+from .openers import ImageOpener\n+\n+\n+class ImageFileError(Exception):\n+    pass\n+\n+\n+class FileBasedHeader(object):\n+    ''' Template class to implement header protocol '''\n+\n+    @classmethod\n+    def from_header(klass, header=None):\n+        if header is None:\n+            return klass()\n+        # I can't do isinstance here because it is not necessarily true\n+        # that a subclass has exactly the same interface as its parent\n+        # - for example Nifti1Images inherit from Analyze, but have\n+        # different field names\n+        if type(header) == klass:\n+            return header.copy()\n+        raise NotImplementedError(\"Header class requires a conversion\"\n+                                  \" from %s to %s\" % (klass, type(header)))\n+\n+    @classmethod\n+    def from_fileobj(klass, fileobj):\n+        raise NotImplementedError\n+\n+    def write_to(self, fileobj):\n+        raise NotImplementedError\n+\n+    def __eq__(self, other):\n+        raise NotImplementedError\n+\n+    def __ne__(self, other):\n+        return not self == other\n+\n+    def copy(self):\n+        ''' Copy object to independent representation\n+\n+        The copy should not be affected by any changes to the original\n+        object.\n+        '''\n+        raise NotImplementedError\n+\n+\n+class FileBasedImage(object):\n+    '''\n+    It also has a ``header`` - some standard set of meta-data that is specific to\n+    the image format, and ``extra`` - a dictionary container for any other\n+    metadata.\n+\n+    It has\n+\n+    attributes:\n+\n+       * extra\n+\n+    properties:\n+\n+       * shape\n+       * affine\n+       * header\n+       * dataobj\n+\n+    You cannot slice an image, and trying to slice an image generates an\n+    informative TypeError.\n+    '''\n+\n+\n+    '''\n+    This abstract image class defines an interface for loading/saving images\n+    from disk. It doesn't define any image properties.\n+\n+    It has\n+\n+\n+    methods:\n+       * .to_filename(fname) - writes data to filename(s) derived from\n+         ``fname``, where the derivation may differ between formats.\n+       * to_file_map() - save image to files with which the image is already\n+         associated.\n+\n+    classmethods:\n+\n+       * from_filename(fname) - make instance by loading from filename\n+       * from_file_map(fmap) - make instance from file map\n+       * instance_to_filename(img, fname) - save ``img`` instance to\n+         filename ``fname``.\n+\n+\n+    There are several ways of writing data.\n+    =======================================\n+\n+    There is the usual way, which is the default::\n+\n+        img.to_filename(fname)\n+\n+    and that is, to take the data encapsulated by the image and cast it to\n+    the datatype the header expects, setting any available header scaling\n+    into the header to help the data match.\n+\n+    You can load the data into an image from file with::\n+\n+       img.from_filename(fname)\n+\n+    The image stores its associated files in its ``file_map`` attribute.  In order\n+    to just save an image, for which you know there is an associated filename, or\n+    other storage, you can do::\n+\n+       img.to_file_map()\n+\n+    You can get the data out again with::\n+\n+        img.get_data()\n+\n+    Less commonly, for some image types that support it, you might want to\n+    fetch out the unscaled array via the object containing the data::\n+\n+        unscaled_data = img.dataoobj.get_unscaled()\n+\n+    Analyze-type images (including nifti) support this, but others may not\n+    (MINC, for example).\n+\n+    Sometimes you might to avoid any loss of precision by making the\n+    data type the same as the input::\n+\n+        hdr = img.header\n+        hdr.set_data_dtype(data.dtype)\n+        img.to_filename(fname)\n+\n+    Files interface\n+    ===============\n+\n+    The image has an attribute ``file_map``.  This is a mapping, that has keys\n+    corresponding to the file types that an image needs for storage.  For\n+    example, the Analyze data format needs an ``image`` and a ``header``\n+    file type for storage:\n+\n+       >>> import nibabel as nib\n+       >>> data = np.arange(24, dtype='f4').reshape((2,3,4))\n+       >>> img = nib.AnalyzeImage(data, np.eye(4))\n+       >>> sorted(img.file_map)\n+       ['header', 'image']\n+\n+    The values of ``file_map`` are not in fact files but objects with\n+    attributes ``filename``, ``fileobj`` and ``pos``.\n+\n+    The reason for this interface, is that the contents of files has to\n+    contain enough information so that an existing image instance can save\n+    itself back to the files pointed to in ``file_map``.  When a file holder\n+    holds active file-like objects, then these may be affected by the\n+    initial file read; in this case, the contains file-like objects need to\n+    carry the position at which a write (with ``to_files``) should place the\n+    data.  The ``file_map`` contents should therefore be such, that this will\n+    work:\n+\n+       >>> # write an image to files\n+       >>> from io import BytesIO\n+       >>> file_map = nib.AnalyzeImage.make_file_map()\n+       >>> file_map['image'].fileobj = BytesIO()\n+       >>> file_map['header'].fileobj = BytesIO()\n+       >>> img = nib.AnalyzeImage(data, np.eye(4))\n+       >>> img.file_map = file_map\n+       >>> img.to_file_map()\n+       >>> # read it back again from the written files\n+       >>> img2 = nib.AnalyzeImage.from_file_map(file_map)\n+       >>> np.all(img2.get_data() == data)\n+       True\n+       >>> # write, read it again\n+       >>> img2.to_file_map()\n+       >>> img3 = nib.AnalyzeImage.from_file_map(file_map)\n+       >>> np.all(img3.get_data() == data)\n+       True\n+    '''\n+    files_types = (('image', None),)\n+    alternate_exts = ()  # Modified by @ImageOpener.register_ext_from_image\n+    _compressed_suffixes = ()\n+    header_class = FileBasedHeader\n+\n+    makeable = True  # Used in test code\n+    rw = True  # Used in test code\n+\n+    def __init__(self, header=None, extra=None, file_map=None):\n+        ''' Initialize image\n+\n+        The image is a combination of (header), with\n+        optional metadata in `extra`, and filename / file-like objects\n+        contained in the `file_map` mapping.\n+\n+        Parameters\n+        ----------\n+        header : None or mapping or header instance, optional\n+           metadata for this image format\n+        extra : None or mapping, optional\n+           metadata to associate with image that cannot be stored in the\n+           metadata of this image type\n+        file_map : mapping, optional\n+           mapping giving file information for this image format\n+        '''\n+\n+        if header or self.header_class:\n+            self._header = self.header_class.from_header(header)\n+        else:\n+            self._header = None\n+        if extra is None:\n+            extra = {}\n+        self.extra = extra\n+\n+        if file_map is None:\n+            file_map = self.__class__.make_file_map()\n+        self.file_map = file_map\n+\n+    @property\n+    def header(self):\n+        return self._header\n+\n+    def __getitem__(self):\n+        ''' No slicing or dictionary interface for images\n+        '''\n+        raise TypeError(\"Cannot slice image objects; consider slicing image \"\n+                        \"array data with `img.dataobj[slice]` or \"\n+                        \"`img.get_data()[slice]`\")\n+\n+    def get_header(self):\n+        \"\"\" Get header from image\n+\n+        Please use the `header` property instead of `get_header`; we will\n+        deprecate this method in future versions of nibabel.\n+        \"\"\"\n+        return self.header\n+\n+    def get_filename(self):\n+        ''' Fetch the image filename\n+\n+        Parameters\n+        ----------\n+        None\n+\n+        Returns\n+        -------\n+        fname : None or str\n+           Returns None if there is no filename, or a filename string.\n+           If an image may have several filenames assoctiated with it\n+           (e.g Analyze ``.img, .hdr`` pair) then we return the more\n+           characteristic filename (the ``.img`` filename in the case of\n+           Analyze')\n+        '''\n+        # which filename is returned depends on the ordering of the\n+        # 'files_types' class attribute - we return the name\n+        # corresponding to the first in that tuple\n+        characteristic_type = self.files_types[0][0]\n+        return self.file_map[characteristic_type].filename\n+\n+    def set_filename(self, filename):\n+        ''' Sets the files in the object from a given filename\n+\n+        The different image formats may check whether the filename has\n+        an extension characteristic of the format, and raise an error if\n+        not.\n+\n+        Parameters\n+        ----------\n+        filename : str\n+           If the image format only has one file associated with it,\n+           this will be the only filename set into the image\n+           ``.file_map`` attribute. Otherwise, the image instance will\n+           try and guess the other filenames from this given filename.\n+        '''\n+        self.file_map = self.__class__.filespec_to_file_map(filename)\n+\n+    @classmethod\n+    def from_filename(klass, filename):\n+        file_map = klass.filespec_to_file_map(filename)\n+        return klass.from_file_map(file_map)\n+\n+    @classmethod\n+    def from_filespec(klass, filespec):\n+        warnings.warn('``from_filespec`` class method is deprecated\\n'\n+                      'Please use the ``from_filename`` class method '\n+                      'instead',\n+                      DeprecationWarning, stacklevel=2)\n+        klass.from_filename(filespec)\n+\n+    @classmethod\n+    def from_file_map(klass, file_map):\n+        raise NotImplementedError\n+\n+    @classmethod\n+    def from_files(klass, file_map):\n+        warnings.warn('``from_files`` class method is deprecated\\n'\n+                      'Please use the ``from_file_map`` class method '\n+                      'instead',\n+                      DeprecationWarning, stacklevel=2)\n+        return klass.from_file_map(file_map)\n+\n+    @classmethod\n+    def filespec_to_file_map(klass, filespec):\n+        \"\"\" Make `file_map` for this class from filename `filespec`\n+\n+        Class method\n+\n+        Parameters\n+        ----------\n+        filespec : str\n+            Filename that might be for this image file type.\n+\n+        Returns\n+        -------\n+        file_map : dict\n+            `file_map` dict with (key, value) pairs of (``file_type``,\n+            FileHolder instance), where ``file_type`` is a string giving the\n+            type of the contained file.\n+\n+        Raises\n+        ------\n+        ImageFileError\n+            if `filespec` is not recognizable as being a filename for this\n+            image type.\n+        \"\"\"\n+        try:\n+            filenames = types_filenames(\n+                filespec, klass.files_types,\n+                trailing_suffixes=klass._compressed_suffixes)\n+        except TypesFilenamesError:\n+            raise ImageFileError(\n+                'Filespec \"{0}\" does not look right for class {1}'.format(\n+                    filespec, klass))\n+        file_map = {}\n+        for key, fname in filenames.items():\n+            file_map[key] = FileHolder(filename=fname)\n+        return file_map\n+\n+    @classmethod\n+    def filespec_to_files(klass, filespec):\n+        warnings.warn('``filespec_to_files`` class method is deprecated\\n'\n+                      'Please use the ``filespec_to_file_map`` class method '\n+                      'instead',\n+                      DeprecationWarning, stacklevel=2)\n+        return klass.filespec_to_file_map(filespec)\n+\n+    def to_filename(self, filename):\n+        ''' Write image to files implied by filename string\n+\n+        Parameters\n+        ----------\n+        filename : str\n+           filename to which to save image.  We will parse `filename`\n+           with ``filespec_to_file_map`` to work out names for image,\n+           header etc.\n+\n+        Returns\n+        -------\n+        None\n+        '''\n+        self.file_map = self.filespec_to_file_map(filename)\n+        self.to_file_map()\n+\n+    def to_filespec(self, filename):\n+        warnings.warn('``to_filespec`` is deprecated, please '\n+                      'use ``to_filename`` instead',\n+                      DeprecationWarning, stacklevel=2)\n+        self.to_filename(filename)\n+\n+    def to_file_map(self, file_map=None):\n+        raise NotImplementedError\n+\n+    def to_files(self, file_map=None):\n+        warnings.warn('``to_files`` method is deprecated\\n'\n+                      'Please use the ``to_file_map`` method '\n+                      'instead',\n+                      DeprecationWarning, stacklevel=2)\n+        self.to_file_map(file_map)\n+\n+    @classmethod\n+    def make_file_map(klass, mapping=None):\n+        ''' Class method to make files holder for this image type\n+\n+        Parameters\n+        ----------\n+        mapping : None or mapping, optional\n+           mapping with keys corresponding to image file types (such as\n+           'image', 'header' etc, depending on image class) and values\n+           that are filenames or file-like.  Default is None\n+\n+        Returns\n+        -------\n+        file_map : dict\n+           dict with string keys given by first entry in tuples in\n+           sequence klass.files_types, and values of type FileHolder,\n+           where FileHolder objects have default values, other than\n+           those given by `mapping`\n+        '''\n+        if mapping is None:\n+            mapping = {}\n+        file_map = {}\n+        for key, ext in klass.files_types:\n+            file_map[key] = FileHolder()\n+            mapval = mapping.get(key, None)\n+            if isinstance(mapval, basestring):\n+                file_map[key].filename = mapval\n+            elif hasattr(mapval, 'tell'):\n+                file_map[key].fileobj = mapval\n+        return file_map\n+\n+    load = from_filename\n+\n+    @classmethod\n+    def instance_to_filename(klass, img, filename):\n+        ''' Save `img` in our own format, to name implied by `filename`\n+\n+        This is a class method\n+\n+        Parameters\n+        ----------\n+        img : ``spatialimage`` instance\n+           In fact, an object with the API of ``spatialimage`` - specifically\n+           ``dataobj``, ``affine``, ``header`` and ``extra``.\n+        filename : str\n+           Filename, implying name to which to save image.\n+        '''\n+        img = klass.from_image(img)\n+        img.to_filename(filename)\n+\n+    @classmethod\n+    def from_image(klass, img):\n+        ''' Class method to create new instance of own class from `img`\n+\n+        Parameters\n+        ----------\n+        img : ``spatialimage`` instance\n+           In fact, an object with the API of ``spatialimage`` -\n+           specifically ``dataobj``, ``affine``, ``header`` and ``extra``.\n+\n+        Returns\n+        -------\n+        cimg : ``spatialimage`` instance\n+           Image, of our own class\n+        '''\n+        return klass(img.dataobj,\n+                     img.affine,\n+                     klass.header_class.from_header(img.header),\n+                     extra=img.extra.copy())","path":"nibabel/filebasedimages.py","position":465,"original_position":465,"commit_id":"a275bc33eb1f584b68acc594f2902649633b9998","original_commit_id":"a275bc33eb1f584b68acc594f2902649633b9998","user":{"login":"bcipolli","id":4072455,"avatar_url":"https://avatars.githubusercontent.com/u/4072455?v=3","gravatar_id":"","url":"https://api.github.com/users/bcipolli","html_url":"https://github.com/bcipolli","followers_url":"https://api.github.com/users/bcipolli/followers","following_url":"https://api.github.com/users/bcipolli/following{/other_user}","gists_url":"https://api.github.com/users/bcipolli/gists{/gist_id}","starred_url":"https://api.github.com/users/bcipolli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bcipolli/subscriptions","organizations_url":"https://api.github.com/users/bcipolli/orgs","repos_url":"https://api.github.com/users/bcipolli/repos","events_url":"https://api.github.com/users/bcipolli/events{/privacy}","received_events_url":"https://api.github.com/users/bcipolli/received_events","type":"User","site_admin":false},"body":"No. Anything with `dataobj` or `affine` should not appear in `filebasedimage`. There are also some dirty docs; fixing this now.","created_at":"2015-10-21T17:44:34Z","updated_at":"2015-10-21T17:44:34Z","html_url":"https://github.com/nipy/nibabel/pull/364#discussion_r42656897","pull_request_url":"https://api.github.com/repos/nipy/nibabel/pulls/364","_links":{"self":{"href":"https://api.github.com/repos/nipy/nibabel/pulls/comments/42656897"},"html":{"href":"https://github.com/nipy/nibabel/pull/364#discussion_r42656897"},"pull_request":{"href":"https://api.github.com/repos/nipy/nibabel/pulls/364"}}},"pull_request":{"url":"https://api.github.com/repos/nipy/nibabel/pulls/364","id":48334649,"html_url":"https://github.com/nipy/nibabel/pull/364","diff_url":"https://github.com/nipy/nibabel/pull/364.diff","patch_url":"https://github.com/nipy/nibabel/pull/364.patch","issue_url":"https://api.github.com/repos/nipy/nibabel/issues/364","number":364,"state":"open","locked":false,"title":"RF: move load/save logic out of SpatialImage","user":{"login":"bcipolli","id":4072455,"avatar_url":"https://avatars.githubusercontent.com/u/4072455?v=3","gravatar_id":"","url":"https://api.github.com/users/bcipolli","html_url":"https://github.com/bcipolli","followers_url":"https://api.github.com/users/bcipolli/followers","following_url":"https://api.github.com/users/bcipolli/following{/other_user}","gists_url":"https://api.github.com/users/bcipolli/gists{/gist_id}","starred_url":"https://api.github.com/users/bcipolli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bcipolli/subscriptions","organizations_url":"https://api.github.com/users/bcipolli/orgs","repos_url":"https://api.github.com/users/bcipolli/repos","events_url":"https://api.github.com/users/bcipolli/events{/privacy}","received_events_url":"https://api.github.com/users/bcipolli/received_events","type":"User","site_admin":false},"body":"This PR precedes #360, which builds off of this work. I'll copy some text from that PR; there's quite a bit of discussion about how this fits into forward-looking design of surface vs. spatial, binary vs. xml object models.\r\n\r\n**Issue:**\r\nThe final issue blocking streamlined GIFTI/CIFTI in nibabel is: image file I/O functions are tied directly to the `SpatialImage` class; non-volumetric images (GIFTI/CIFTI) can't extend `SpatialImage` and so can't easily get in the `load`/`save` patheway.\r\n\r\n**Proposed Solution:**\r\nThe good news is, the file I/O code actually is quite general. We can easily abstract the file I/O into a higher-level class (which I've called `FileBasedHeader`/`FileBasedImage`, as the more generic `Header` was already taken) that `SpatialImage` and `GiftiImage` inherit from.\r\n\r\n**To do:**\r\n- [X] Move file I/O from `SpatialImage` into `FileBasedHeader`/`FileBasedImage` \r\n- [X] Update tests that assume all image types are of type `SpatialImage`\r\n\r\n**Notes:**\r\n* Given the above design, I believe this is mostly done. Rather than fine-tuning the code now, I'd rather get ideas about the overall design first. @matthew-brett and @effigies , your input is most welcome when you have the time!\r\n* Whatever we decide here, I'll be using to push forward GIFTI (#360) and CIFTI (#249) support","created_at":"2015-10-21T13:58:58Z","updated_at":"2015-10-21T17:44:34Z","closed_at":null,"merged_at":null,"merge_commit_sha":"617e71690574061d77d1fbd37fb82eedd69cd9bc","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/nipy/nibabel/pulls/364/commits","review_comments_url":"https://api.github.com/repos/nipy/nibabel/pulls/364/comments","review_comment_url":"https://api.github.com/repos/nipy/nibabel/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nipy/nibabel/issues/364/comments","statuses_url":"https://api.github.com/repos/nipy/nibabel/statuses/a275bc33eb1f584b68acc594f2902649633b9998","head":{"label":"bcipolli:filebasedimage","ref":"filebasedimage","sha":"a275bc33eb1f584b68acc594f2902649633b9998","user":{"login":"bcipolli","id":4072455,"avatar_url":"https://avatars.githubusercontent.com/u/4072455?v=3","gravatar_id":"","url":"https://api.github.com/users/bcipolli","html_url":"https://github.com/bcipolli","followers_url":"https://api.github.com/users/bcipolli/followers","following_url":"https://api.github.com/users/bcipolli/following{/other_user}","gists_url":"https://api.github.com/users/bcipolli/gists{/gist_id}","starred_url":"https://api.github.com/users/bcipolli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bcipolli/subscriptions","organizations_url":"https://api.github.com/users/bcipolli/orgs","repos_url":"https://api.github.com/users/bcipolli/repos","events_url":"https://api.github.com/users/bcipolli/events{/privacy}","received_events_url":"https://api.github.com/users/bcipolli/received_events","type":"User","site_admin":false},"repo":{"id":31253147,"name":"nibabel","full_name":"bcipolli/nibabel","owner":{"login":"bcipolli","id":4072455,"avatar_url":"https://avatars.githubusercontent.com/u/4072455?v=3","gravatar_id":"","url":"https://api.github.com/users/bcipolli","html_url":"https://github.com/bcipolli","followers_url":"https://api.github.com/users/bcipolli/followers","following_url":"https://api.github.com/users/bcipolli/following{/other_user}","gists_url":"https://api.github.com/users/bcipolli/gists{/gist_id}","starred_url":"https://api.github.com/users/bcipolli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bcipolli/subscriptions","organizations_url":"https://api.github.com/users/bcipolli/orgs","repos_url":"https://api.github.com/users/bcipolli/repos","events_url":"https://api.github.com/users/bcipolli/events{/privacy}","received_events_url":"https://api.github.com/users/bcipolli/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bcipolli/nibabel","description":"Python package to access a cacophony of neuro-imaging file formats","fork":true,"url":"https://api.github.com/repos/bcipolli/nibabel","forks_url":"https://api.github.com/repos/bcipolli/nibabel/forks","keys_url":"https://api.github.com/repos/bcipolli/nibabel/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bcipolli/nibabel/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bcipolli/nibabel/teams","hooks_url":"https://api.github.com/repos/bcipolli/nibabel/hooks","issue_events_url":"https://api.github.com/repos/bcipolli/nibabel/issues/events{/number}","events_url":"https://api.github.com/repos/bcipolli/nibabel/events","assignees_url":"https://api.github.com/repos/bcipolli/nibabel/assignees{/user}","branches_url":"https://api.github.com/repos/bcipolli/nibabel/branches{/branch}","tags_url":"https://api.github.com/repos/bcipolli/nibabel/tags","blobs_url":"https://api.github.com/repos/bcipolli/nibabel/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bcipolli/nibabel/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bcipolli/nibabel/git/refs{/sha}","trees_url":"https://api.github.com/repos/bcipolli/nibabel/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bcipolli/nibabel/statuses/{sha}","languages_url":"https://api.github.com/repos/bcipolli/nibabel/languages","stargazers_url":"https://api.github.com/repos/bcipolli/nibabel/stargazers","contributors_url":"https://api.github.com/repos/bcipolli/nibabel/contributors","subscribers_url":"https://api.github.com/repos/bcipolli/nibabel/subscribers","subscription_url":"https://api.github.com/repos/bcipolli/nibabel/subscription","commits_url":"https://api.github.com/repos/bcipolli/nibabel/commits{/sha}","git_commits_url":"https://api.github.com/repos/bcipolli/nibabel/git/commits{/sha}","comments_url":"https://api.github.com/repos/bcipolli/nibabel/comments{/number}","issue_comment_url":"https://api.github.com/repos/bcipolli/nibabel/issues/comments{/number}","contents_url":"https://api.github.com/repos/bcipolli/nibabel/contents/{+path}","compare_url":"https://api.github.com/repos/bcipolli/nibabel/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bcipolli/nibabel/merges","archive_url":"https://api.github.com/repos/bcipolli/nibabel/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bcipolli/nibabel/downloads","issues_url":"https://api.github.com/repos/bcipolli/nibabel/issues{/number}","pulls_url":"https://api.github.com/repos/bcipolli/nibabel/pulls{/number}","milestones_url":"https://api.github.com/repos/bcipolli/nibabel/milestones{/number}","notifications_url":"https://api.github.com/repos/bcipolli/nibabel/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bcipolli/nibabel/labels{/name}","releases_url":"https://api.github.com/repos/bcipolli/nibabel/releases{/id}","created_at":"2015-02-24T09:35:11Z","updated_at":"2015-02-24T09:35:13Z","pushed_at":"2015-10-21T15:01:15Z","git_url":"git://github.com/bcipolli/nibabel.git","ssh_url":"git@github.com:bcipolli/nibabel.git","clone_url":"https://github.com/bcipolli/nibabel.git","svn_url":"https://github.com/bcipolli/nibabel","homepage":"http://nipy.org/nibabel/","size":8608,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"nipy:master","ref":"master","sha":"63f9ef26d4c1564fb0e193c674a81f67f8972b04","user":{"login":"nipy","id":233707,"avatar_url":"https://avatars.githubusercontent.com/u/233707?v=3","gravatar_id":"","url":"https://api.github.com/users/nipy","html_url":"https://github.com/nipy","followers_url":"https://api.github.com/users/nipy/followers","following_url":"https://api.github.com/users/nipy/following{/other_user}","gists_url":"https://api.github.com/users/nipy/gists{/gist_id}","starred_url":"https://api.github.com/users/nipy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nipy/subscriptions","organizations_url":"https://api.github.com/users/nipy/orgs","repos_url":"https://api.github.com/users/nipy/repos","events_url":"https://api.github.com/users/nipy/events{/privacy}","received_events_url":"https://api.github.com/users/nipy/received_events","type":"Organization","site_admin":false},"repo":{"id":791352,"name":"nibabel","full_name":"nipy/nibabel","owner":{"login":"nipy","id":233707,"avatar_url":"https://avatars.githubusercontent.com/u/233707?v=3","gravatar_id":"","url":"https://api.github.com/users/nipy","html_url":"https://github.com/nipy","followers_url":"https://api.github.com/users/nipy/followers","following_url":"https://api.github.com/users/nipy/following{/other_user}","gists_url":"https://api.github.com/users/nipy/gists{/gist_id}","starred_url":"https://api.github.com/users/nipy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nipy/subscriptions","organizations_url":"https://api.github.com/users/nipy/orgs","repos_url":"https://api.github.com/users/nipy/repos","events_url":"https://api.github.com/users/nipy/events{/privacy}","received_events_url":"https://api.github.com/users/nipy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nipy/nibabel","description":"Python package to access a cacophony of neuro-imaging file formats","fork":false,"url":"https://api.github.com/repos/nipy/nibabel","forks_url":"https://api.github.com/repos/nipy/nibabel/forks","keys_url":"https://api.github.com/repos/nipy/nibabel/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nipy/nibabel/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nipy/nibabel/teams","hooks_url":"https://api.github.com/repos/nipy/nibabel/hooks","issue_events_url":"https://api.github.com/repos/nipy/nibabel/issues/events{/number}","events_url":"https://api.github.com/repos/nipy/nibabel/events","assignees_url":"https://api.github.com/repos/nipy/nibabel/assignees{/user}","branches_url":"https://api.github.com/repos/nipy/nibabel/branches{/branch}","tags_url":"https://api.github.com/repos/nipy/nibabel/tags","blobs_url":"https://api.github.com/repos/nipy/nibabel/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nipy/nibabel/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nipy/nibabel/git/refs{/sha}","trees_url":"https://api.github.com/repos/nipy/nibabel/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nipy/nibabel/statuses/{sha}","languages_url":"https://api.github.com/repos/nipy/nibabel/languages","stargazers_url":"https://api.github.com/repos/nipy/nibabel/stargazers","contributors_url":"https://api.github.com/repos/nipy/nibabel/contributors","subscribers_url":"https://api.github.com/repos/nipy/nibabel/subscribers","subscription_url":"https://api.github.com/repos/nipy/nibabel/subscription","commits_url":"https://api.github.com/repos/nipy/nibabel/commits{/sha}","git_commits_url":"https://api.github.com/repos/nipy/nibabel/git/commits{/sha}","comments_url":"https://api.github.com/repos/nipy/nibabel/comments{/number}","issue_comment_url":"https://api.github.com/repos/nipy/nibabel/issues/comments{/number}","contents_url":"https://api.github.com/repos/nipy/nibabel/contents/{+path}","compare_url":"https://api.github.com/repos/nipy/nibabel/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nipy/nibabel/merges","archive_url":"https://api.github.com/repos/nipy/nibabel/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nipy/nibabel/downloads","issues_url":"https://api.github.com/repos/nipy/nibabel/issues{/number}","pulls_url":"https://api.github.com/repos/nipy/nibabel/pulls{/number}","milestones_url":"https://api.github.com/repos/nipy/nibabel/milestones{/number}","notifications_url":"https://api.github.com/repos/nipy/nibabel/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nipy/nibabel/labels{/name}","releases_url":"https://api.github.com/repos/nipy/nibabel/releases{/id}","created_at":"2010-07-22T16:28:30Z","updated_at":"2015-10-06T06:58:39Z","pushed_at":"2015-10-21T16:19:14Z","git_url":"git://github.com/nipy/nibabel.git","ssh_url":"git@github.com:nipy/nibabel.git","clone_url":"https://github.com/nipy/nibabel.git","svn_url":"https://github.com/nipy/nibabel","homepage":"http://nipy.org/nibabel/","size":17893,"stargazers_count":96,"watchers_count":96,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":95,"mirror_url":null,"open_issues_count":58,"forks":95,"open_issues":58,"watchers":96,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nipy/nibabel/pulls/364"},"html":{"href":"https://github.com/nipy/nibabel/pull/364"},"issue":{"href":"https://api.github.com/repos/nipy/nibabel/issues/364"},"comments":{"href":"https://api.github.com/repos/nipy/nibabel/issues/364/comments"},"review_comments":{"href":"https://api.github.com/repos/nipy/nibabel/pulls/364/comments"},"review_comment":{"href":"https://api.github.com/repos/nipy/nibabel/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nipy/nibabel/pulls/364/commits"},"statuses":{"href":"https://api.github.com/repos/nipy/nibabel/statuses/a275bc33eb1f584b68acc594f2902649633b9998"}}}}