{"action":"created","comment":{"url":"https://api.github.com/repos/Toblerity/Fiona/pulls/comments/221425780","pull_request_review_id":160078147,"id":221425780,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyMTQyNTc4MA==","diff_hunk":"@@ -0,0 +1,377 @@\n+\"\"\"Rasterio's GDAL/AWS environment\"\"\"\n+\n+from functools import wraps\n+import logging\n+import threading\n+\n+from fiona._env import (\n+    GDALEnv, get_gdal_config, set_gdal_config)\n+from fiona.errors import EnvError\n+from fiona.session import Session, AWSSession, DummySession\n+\n+\n+class ThreadEnv(threading.local):\n+    def __init__(self):\n+        self._env = None  # Initialises in each thread\n+\n+        # When the outermost 'rasterio.Env()' executes '__enter__' it\n+        # probes the GDAL environment to see if any of the supplied\n+        # config options already exist, the assumption being that they\n+        # were set with 'osgeo.gdal.SetConfigOption()' or possibly\n+        # 'rasterio.env.set_gdal_config()'.  The discovered options are\n+        # reinstated when the outermost Rasterio environment exits.\n+        # Without this check any environment options that are present in\n+        # the GDAL environment and are also passed to 'rasterio.Env()'\n+        # will be unset when 'rasterio.Env()' tears down, regardless of\n+        # their value.  For example:\n+        #\n+        #   from osgeo import gdal import rasterio\n+        #\n+        #   gdal.SetConfigOption('key', 'value') with\n+        #   rasterio.Env(key='something'): pass\n+        #\n+        # The config option 'key' would be unset when 'Env()' exits.\n+        # A more comprehensive solution would also leverage\n+        # https://trac.osgeo.org/gdal/changeset/37273 but this gets\n+        # Rasterio + older versions of GDAL halfway there.  One major\n+        # assumption is that environment variables are not set directly\n+        # with 'osgeo.gdal.SetConfigOption()' OR\n+        # 'rasterio.env.set_gdal_config()' inside of a 'rasterio.Env()'.\n+        self._discovered_options = None\n+\n+\n+local = ThreadEnv()\n+\n+log = logging.getLogger(__name__)\n+\n+\n+class Env(object):\n+    \"\"\"Abstraction for GDAL and AWS configuration\n+\n+    The GDAL library is stateful: it has a registry of format drivers,\n+    an error stack, and dozens of configuration options.\n+\n+    Rasterio's approach to working with GDAL is to wrap all the state\n+    up using a Python context manager (see PEP 343,\n+    https://www.python.org/dev/peps/pep-0343/). When the context is\n+    entered GDAL drivers are registered, error handlers are\n+    configured, and configuration options are set. When the context\n+    is exited, drivers are removed from the registry and other\n+    configurations are removed.\n+\n+    Example:\n+\n+        with rasterio.Env(GDAL_CACHEMAX=512) as env:\n+            # All drivers are registered, GDAL's raster block cache\n+            # size is set to 512MB.\n+            # Commence processing...\n+            ...\n+            # End of processing.\n+\n+        # At this point, configuration options are set to their\n+        # previous (possible unset) values.\n+\n+    A boto3 session or boto3 session constructor arguments\n+    `aws_access_key_id`, `aws_secret_access_key`, `aws_session_token`\n+    may be passed to Env's constructor. In the latter case, a session\n+    will be created as soon as needed. AWS credentials are configured\n+    for GDAL as needed.\n+    \"\"\"\n+\n+    @classmethod\n+    def default_options(cls):\n+        \"\"\"Default configuration options\n+\n+        Parameters\n+        ----------\n+        None\n+\n+        Returns\n+        -------\n+        dict\n+        \"\"\"\n+        return {\n+            'CHECK_WITH_INVERT_PROJ': True,\n+            'GTIFF_IMPLICIT_JPEG_OVR': False,\n+            \"RASTERIO_ENV\": True\n+        }\n+\n+    def __init__(\n+            self, session=None, aws_unsigned=False, aws_access_key_id=None,\n+            aws_secret_access_key=None, aws_session_token=None,\n+            region_name=None, profile_name=None, session_class=AWSSession,\n+            **options):\n+        \"\"\"Create a new GDAL/AWS environment.\n+\n+        Note: this class is a context manager. GDAL isn't configured\n+        until the context is entered via `with rasterio.Env():`\n+\n+        Parameters\n+        ----------\n+        session : optional\n+            A Session object.\n+        aws_unsigned : bool, optional (default: False)\n+            If True, requests will be unsigned.\n+        aws_access_key_id : str, optional\n+            An access key id, as per boto3.\n+        aws_secret_access_key : str, optional\n+            A secret access key, as per boto3.\n+        aws_session_token : str, optional\n+            A session token, as per boto3.\n+        region_name : str, optional\n+            A region name, as per boto3.\n+        profile_name : str, optional\n+            A shared credentials profile name, as per boto3.\n+        session_class : Session, optional\n+            A sub-class of Session.\n+        **options : optional\n+            A mapping of GDAL configuration options, e.g.,\n+            `CPL_DEBUG=True, CHECK_WITH_INVERT_PROJ=False`.\n+\n+        Returns\n+        -------\n+        Env\n+\n+        Notes\n+        -----\n+        We raise EnvError if the GDAL config options\n+        AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY are given. AWS\n+        credentials are handled exclusively by boto3.\n+\n+        Examples\n+        --------\n+\n+        >>> with Env(CPL_DEBUG=True, CPL_CURL_VERBOSE=True):\n+        ...     with rasterio.open(\"https://example.com/a.tif\") as src:\n+        ...         print(src.profile)\n+\n+        For access to secured cloud resources, a Rasterio Session or a\n+        foreign session object may be passed to the constructor.\n+\n+        >>> import boto3\n+        >>> from rasterio.session import AWSSession\n+        >>> boto3_session = boto3.Session(...)\n+        >>> with Env(AWSSession(boto3_session)):\n+        ...     with rasterio.open(\"s3://mybucket/a.tif\") as src:\n+        ...         print(src.profile)\n+\n+        \"\"\"\n+        if ('AWS_ACCESS_KEY_ID' in options or\n+                'AWS_SECRET_ACCESS_KEY' in options):\n+            raise EnvError(\n+                \"GDAL's AWS config options can not be directly set. \"\n+                \"AWS credentials are handled exclusively by boto3.\")\n+\n+        if session:\n+            self.session = session\n+        else:\n+            self.session = DummySession()\n+\n+        self.options = options.copy()\n+        self.context_options = {}\n+\n+    @classmethod\n+    def from_defaults(cls, *args, **kwargs):\n+        \"\"\"Create an environment with default config options\n+\n+        Parameters\n+        ----------\n+        args : optional\n+            Positional arguments for Env()\n+        kwargs : optional\n+            Keyword arguments for Env()\n+\n+        Returns\n+        -------\n+        Env\n+\n+        Notes\n+        -----\n+        The items in kwargs will be overlaid on the default values.\n+\n+        \"\"\"\n+        options = Env.default_options()\n+        options.update(**kwargs)\n+        return Env(*args, **options)\n+\n+    @property\n+    def is_credentialized(self):\n+        \"\"\"Test for existence of cloud credentials\n+\n+        Returns\n+        -------\n+        bool\n+        \"\"\"\n+        return hascreds()  # bool(self.session)\n+\n+    def credentialize(self):\n+        \"\"\"Get credentials and configure GDAL\n+\n+        Note well: this method is a no-op if the GDAL environment\n+        already has credentials, unless session is not None.\n+\n+        Returns\n+        -------\n+        None\n+\n+        \"\"\"\n+        if hascreds():\n+            pass\n+        else:\n+            cred_opts = self.session.get_credential_options()\n+            self.options.update(**cred_opts)\n+            setenv(**cred_opts)\n+\n+    def drivers(self):\n+        \"\"\"Return a mapping of registered drivers.\"\"\"\n+        return local._env.drivers()\n+\n+    def __enter__(self):\n+        log.debug(\"Entering env context: %r\", self)\n+        if local._env is None:\n+            log.debug(\"Starting outermost env\")\n+            self._has_parent_env = False\n+\n+            # See note directly above where _discovered_options is globally\n+            # defined.  This MUST happen before calling 'defenv()'.\n+            local._discovered_options = {}\n+            # Don't want to reinstate the \"RASTERIO_ENV\" option.\n+            probe_env = {k for k in self.options.keys() if k != \"RASTERIO_ENV\"}\n+            for key in probe_env:\n+                val = get_gdal_config(key, normalize=False)\n+                if val is not None:\n+                    local._discovered_options[key] = val\n+                    log.debug(\"Discovered option: %s=%s\", key, val)\n+\n+            defenv(**self.options)\n+            self.context_options = {}\n+        else:\n+            self._has_parent_env = True\n+            self.context_options = getenv()\n+            setenv(**self.options)\n+\n+        self.credentialize()\n+\n+        log.debug(\"Entered env context: %r\", self)\n+        return self\n+\n+    def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):\n+        log.debug(\"Exiting env context: %r\", self)\n+        delenv()\n+        if self._has_parent_env:\n+            defenv()\n+            setenv(**self.context_options)\n+        else:\n+            log.debug(\"Exiting outermost env\")\n+            # See note directly above where _discovered_options is globally\n+            # defined.\n+            while local._discovered_options:\n+                key, val = local._discovered_options.popitem()\n+                set_gdal_config(key, val, normalize=False)\n+                log.debug(\n+                    \"Set discovered option back to: '%s=%s\", key, val)\n+            local._discovered_options = None\n+        log.debug(\"Exited env context: %r\", self)\n+\n+\n+def defenv(**options):\n+    \"\"\"Create a default environment if necessary.\"\"\"\n+    if local._env:\n+        log.debug(\"GDAL environment exists: %r\", local._env)\n+    else:\n+        log.debug(\"No GDAL environment exists\")\n+        local._env = GDALEnv()\n+        local._env.update_config_options(**options)\n+        log.debug(\n+            \"New GDAL environment %r created\", local._env)\n+    local._env.start()\n+\n+\n+def getenv():\n+    \"\"\"Get a mapping of current options.\"\"\"\n+    if not local._env:\n+        raise EnvError(\"No GDAL environment exists\")\n+    else:\n+        log.debug(\"Got a copy of environment %r options\", local._env)\n+        return local._env.options.copy()\n+\n+\n+def hasenv():\n+    return bool(local._env)\n+\n+\n+def setenv(**options):\n+    \"\"\"Set options in the existing environment.\"\"\"\n+    if not local._env:\n+        raise EnvError(\"No GDAL environment exists\")\n+    else:\n+        local._env.update_config_options(**options)\n+        log.debug(\"Updated existing %r with options %r\", local._env, options)\n+\n+\n+def hascreds():\n+    gdal_config = local._env.get_config_options()\n+    return bool('AWS_ACCESS_KEY_ID' in gdal_config and\n+                'AWS_SECRET_ACCESS_KEY' in gdal_config)\n+\n+\n+def delenv():\n+    \"\"\"Delete options in the existing environment.\"\"\"\n+    if not local._env:\n+        raise EnvError(\"No GDAL environment exists\")\n+    else:\n+        local._env.clear_config_options()\n+        log.debug(\"Cleared existing %r options\", local._env)\n+    local._env.stop()\n+    local._env = None\n+\n+\n+def ensure_env(f):\n+    \"\"\"A decorator that ensures an env exists before a function\n+    calls any GDAL C functions.\"\"\"\n+    @wraps(f)\n+    def wrapper(*args, **kwds):\n+        if local._env:\n+            return f(*args, **kwds)\n+        else:\n+            with Env.from_defaults():\n+                return f(*args, **kwds)\n+    return wrapper\n+\n+\n+def ensure_env_credentialled(f):","path":"fiona/env.py","position":342,"original_position":342,"commit_id":"8dd299db5e7a995aa13133c3c9db09cbcc804d85","original_commit_id":"8dd299db5e7a995aa13133c3c9db09cbcc804d85","user":{"login":"snorfalorpagus","id":3883947,"node_id":"MDQ6VXNlcjM4ODM5NDc=","avatar_url":"https://avatars0.githubusercontent.com/u/3883947?v=4","gravatar_id":"","url":"https://api.github.com/users/snorfalorpagus","html_url":"https://github.com/snorfalorpagus","followers_url":"https://api.github.com/users/snorfalorpagus/followers","following_url":"https://api.github.com/users/snorfalorpagus/following{/other_user}","gists_url":"https://api.github.com/users/snorfalorpagus/gists{/gist_id}","starred_url":"https://api.github.com/users/snorfalorpagus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/snorfalorpagus/subscriptions","organizations_url":"https://api.github.com/users/snorfalorpagus/orgs","repos_url":"https://api.github.com/users/snorfalorpagus/repos","events_url":"https://api.github.com/users/snorfalorpagus/events{/privacy}","received_events_url":"https://api.github.com/users/snorfalorpagus/received_events","type":"User","site_admin":false},"body":"Or \"ensure_env_with_credentials\"","created_at":"2018-09-29T11:45:49Z","updated_at":"2018-09-29T11:45:49Z","html_url":"https://github.com/Toblerity/Fiona/pull/629#discussion_r221425780","pull_request_url":"https://api.github.com/repos/Toblerity/Fiona/pulls/629","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/Toblerity/Fiona/pulls/comments/221425780"},"html":{"href":"https://github.com/Toblerity/Fiona/pull/629#discussion_r221425780"},"pull_request":{"href":"https://api.github.com/repos/Toblerity/Fiona/pulls/629"}},"in_reply_to_id":221425404},"pull_request":{"url":"https://api.github.com/repos/Toblerity/Fiona/pulls/629","id":217489238,"node_id":"MDExOlB1bGxSZXF1ZXN0MjE3NDg5MjM4","html_url":"https://github.com/Toblerity/Fiona/pull/629","diff_url":"https://github.com/Toblerity/Fiona/pull/629.diff","patch_url":"https://github.com/Toblerity/Fiona/pull/629.patch","issue_url":"https://api.github.com/repos/Toblerity/Fiona/issues/629","number":629,"state":"open","locked":false,"title":"Port rasterio's env and session","user":{"login":"sgillies","id":33697,"node_id":"MDQ6VXNlcjMzNjk3","avatar_url":"https://avatars2.githubusercontent.com/u/33697?v=4","gravatar_id":"","url":"https://api.github.com/users/sgillies","html_url":"https://github.com/sgillies","followers_url":"https://api.github.com/users/sgillies/followers","following_url":"https://api.github.com/users/sgillies/following{/other_user}","gists_url":"https://api.github.com/users/sgillies/gists{/gist_id}","starred_url":"https://api.github.com/users/sgillies/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sgillies/subscriptions","organizations_url":"https://api.github.com/users/sgillies/orgs","repos_url":"https://api.github.com/users/sgillies/repos","events_url":"https://api.github.com/users/sgillies/events{/privacy}","received_events_url":"https://api.github.com/users/sgillies/received_events","type":"User","site_admin":false},"body":"I've copied Rasterio env, session, and path modules straight over and adjusted the Fiona modules and CLI. There's no attempt to test or use Google Cloud Storage, but it is at the very least not far from working.\r\n\r\nResolves #507","created_at":"2018-09-23T02:39:19Z","updated_at":"2018-09-29T11:45:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6320a609e5c6eab1455a83a41d73de4787064a78","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/Toblerity/Fiona/pulls/629/commits","review_comments_url":"https://api.github.com/repos/Toblerity/Fiona/pulls/629/comments","review_comment_url":"https://api.github.com/repos/Toblerity/Fiona/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Toblerity/Fiona/issues/629/comments","statuses_url":"https://api.github.com/repos/Toblerity/Fiona/statuses/8dd299db5e7a995aa13133c3c9db09cbcc804d85","head":{"label":"Toblerity:issue507","ref":"issue507","sha":"8dd299db5e7a995aa13133c3c9db09cbcc804d85","user":{"login":"Toblerity","id":859968,"node_id":"MDEyOk9yZ2FuaXphdGlvbjg1OTk2OA==","avatar_url":"https://avatars0.githubusercontent.com/u/859968?v=4","gravatar_id":"","url":"https://api.github.com/users/Toblerity","html_url":"https://github.com/Toblerity","followers_url":"https://api.github.com/users/Toblerity/followers","following_url":"https://api.github.com/users/Toblerity/following{/other_user}","gists_url":"https://api.github.com/users/Toblerity/gists{/gist_id}","starred_url":"https://api.github.com/users/Toblerity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Toblerity/subscriptions","organizations_url":"https://api.github.com/users/Toblerity/orgs","repos_url":"https://api.github.com/users/Toblerity/repos","events_url":"https://api.github.com/users/Toblerity/events{/privacy}","received_events_url":"https://api.github.com/users/Toblerity/received_events","type":"Organization","site_admin":false},"repo":{"id":3080628,"node_id":"MDEwOlJlcG9zaXRvcnkzMDgwNjI4","name":"Fiona","full_name":"Toblerity/Fiona","private":false,"owner":{"login":"Toblerity","id":859968,"node_id":"MDEyOk9yZ2FuaXphdGlvbjg1OTk2OA==","avatar_url":"https://avatars0.githubusercontent.com/u/859968?v=4","gravatar_id":"","url":"https://api.github.com/users/Toblerity","html_url":"https://github.com/Toblerity","followers_url":"https://api.github.com/users/Toblerity/followers","following_url":"https://api.github.com/users/Toblerity/following{/other_user}","gists_url":"https://api.github.com/users/Toblerity/gists{/gist_id}","starred_url":"https://api.github.com/users/Toblerity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Toblerity/subscriptions","organizations_url":"https://api.github.com/users/Toblerity/orgs","repos_url":"https://api.github.com/users/Toblerity/repos","events_url":"https://api.github.com/users/Toblerity/events{/privacy}","received_events_url":"https://api.github.com/users/Toblerity/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/Toblerity/Fiona","description":"Fiona is OGR's neat and nimble API","fork":false,"url":"https://api.github.com/repos/Toblerity/Fiona","forks_url":"https://api.github.com/repos/Toblerity/Fiona/forks","keys_url":"https://api.github.com/repos/Toblerity/Fiona/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Toblerity/Fiona/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Toblerity/Fiona/teams","hooks_url":"https://api.github.com/repos/Toblerity/Fiona/hooks","issue_events_url":"https://api.github.com/repos/Toblerity/Fiona/issues/events{/number}","events_url":"https://api.github.com/repos/Toblerity/Fiona/events","assignees_url":"https://api.github.com/repos/Toblerity/Fiona/assignees{/user}","branches_url":"https://api.github.com/repos/Toblerity/Fiona/branches{/branch}","tags_url":"https://api.github.com/repos/Toblerity/Fiona/tags","blobs_url":"https://api.github.com/repos/Toblerity/Fiona/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Toblerity/Fiona/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Toblerity/Fiona/git/refs{/sha}","trees_url":"https://api.github.com/repos/Toblerity/Fiona/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Toblerity/Fiona/statuses/{sha}","languages_url":"https://api.github.com/repos/Toblerity/Fiona/languages","stargazers_url":"https://api.github.com/repos/Toblerity/Fiona/stargazers","contributors_url":"https://api.github.com/repos/Toblerity/Fiona/contributors","subscribers_url":"https://api.github.com/repos/Toblerity/Fiona/subscribers","subscription_url":"https://api.github.com/repos/Toblerity/Fiona/subscription","commits_url":"https://api.github.com/repos/Toblerity/Fiona/commits{/sha}","git_commits_url":"https://api.github.com/repos/Toblerity/Fiona/git/commits{/sha}","comments_url":"https://api.github.com/repos/Toblerity/Fiona/comments{/number}","issue_comment_url":"https://api.github.com/repos/Toblerity/Fiona/issues/comments{/number}","contents_url":"https://api.github.com/repos/Toblerity/Fiona/contents/{+path}","compare_url":"https://api.github.com/repos/Toblerity/Fiona/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Toblerity/Fiona/merges","archive_url":"https://api.github.com/repos/Toblerity/Fiona/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Toblerity/Fiona/downloads","issues_url":"https://api.github.com/repos/Toblerity/Fiona/issues{/number}","pulls_url":"https://api.github.com/repos/Toblerity/Fiona/pulls{/number}","milestones_url":"https://api.github.com/repos/Toblerity/Fiona/milestones{/number}","notifications_url":"https://api.github.com/repos/Toblerity/Fiona/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Toblerity/Fiona/labels{/name}","releases_url":"https://api.github.com/repos/Toblerity/Fiona/releases{/id}","deployments_url":"https://api.github.com/repos/Toblerity/Fiona/deployments","created_at":"2011-12-31T19:47:00Z","updated_at":"2018-09-28T17:14:55Z","pushed_at":"2018-09-27T14:31:49Z","git_url":"git://github.com/Toblerity/Fiona.git","ssh_url":"git@github.com:Toblerity/Fiona.git","clone_url":"https://github.com/Toblerity/Fiona.git","svn_url":"https://github.com/Toblerity/Fiona","homepage":"http://toblerity.github.com/fiona/","size":2039,"stargazers_count":464,"watchers_count":464,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":100,"mirror_url":null,"archived":false,"open_issues_count":50,"license":{"key":"other","name":"NOASSERTION","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":100,"open_issues":50,"watchers":464,"default_branch":"master"}},"base":{"label":"Toblerity:master","ref":"master","sha":"90776a5f38352786603d0127156c4b76df5cae46","user":{"login":"Toblerity","id":859968,"node_id":"MDEyOk9yZ2FuaXphdGlvbjg1OTk2OA==","avatar_url":"https://avatars0.githubusercontent.com/u/859968?v=4","gravatar_id":"","url":"https://api.github.com/users/Toblerity","html_url":"https://github.com/Toblerity","followers_url":"https://api.github.com/users/Toblerity/followers","following_url":"https://api.github.com/users/Toblerity/following{/other_user}","gists_url":"https://api.github.com/users/Toblerity/gists{/gist_id}","starred_url":"https://api.github.com/users/Toblerity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Toblerity/subscriptions","organizations_url":"https://api.github.com/users/Toblerity/orgs","repos_url":"https://api.github.com/users/Toblerity/repos","events_url":"https://api.github.com/users/Toblerity/events{/privacy}","received_events_url":"https://api.github.com/users/Toblerity/received_events","type":"Organization","site_admin":false},"repo":{"id":3080628,"node_id":"MDEwOlJlcG9zaXRvcnkzMDgwNjI4","name":"Fiona","full_name":"Toblerity/Fiona","private":false,"owner":{"login":"Toblerity","id":859968,"node_id":"MDEyOk9yZ2FuaXphdGlvbjg1OTk2OA==","avatar_url":"https://avatars0.githubusercontent.com/u/859968?v=4","gravatar_id":"","url":"https://api.github.com/users/Toblerity","html_url":"https://github.com/Toblerity","followers_url":"https://api.github.com/users/Toblerity/followers","following_url":"https://api.github.com/users/Toblerity/following{/other_user}","gists_url":"https://api.github.com/users/Toblerity/gists{/gist_id}","starred_url":"https://api.github.com/users/Toblerity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Toblerity/subscriptions","organizations_url":"https://api.github.com/users/Toblerity/orgs","repos_url":"https://api.github.com/users/Toblerity/repos","events_url":"https://api.github.com/users/Toblerity/events{/privacy}","received_events_url":"https://api.github.com/users/Toblerity/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/Toblerity/Fiona","description":"Fiona is OGR's neat and nimble API","fork":false,"url":"https://api.github.com/repos/Toblerity/Fiona","forks_url":"https://api.github.com/repos/Toblerity/Fiona/forks","keys_url":"https://api.github.com/repos/Toblerity/Fiona/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Toblerity/Fiona/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Toblerity/Fiona/teams","hooks_url":"https://api.github.com/repos/Toblerity/Fiona/hooks","issue_events_url":"https://api.github.com/repos/Toblerity/Fiona/issues/events{/number}","events_url":"https://api.github.com/repos/Toblerity/Fiona/events","assignees_url":"https://api.github.com/repos/Toblerity/Fiona/assignees{/user}","branches_url":"https://api.github.com/repos/Toblerity/Fiona/branches{/branch}","tags_url":"https://api.github.com/repos/Toblerity/Fiona/tags","blobs_url":"https://api.github.com/repos/Toblerity/Fiona/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Toblerity/Fiona/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Toblerity/Fiona/git/refs{/sha}","trees_url":"https://api.github.com/repos/Toblerity/Fiona/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Toblerity/Fiona/statuses/{sha}","languages_url":"https://api.github.com/repos/Toblerity/Fiona/languages","stargazers_url":"https://api.github.com/repos/Toblerity/Fiona/stargazers","contributors_url":"https://api.github.com/repos/Toblerity/Fiona/contributors","subscribers_url":"https://api.github.com/repos/Toblerity/Fiona/subscribers","subscription_url":"https://api.github.com/repos/Toblerity/Fiona/subscription","commits_url":"https://api.github.com/repos/Toblerity/Fiona/commits{/sha}","git_commits_url":"https://api.github.com/repos/Toblerity/Fiona/git/commits{/sha}","comments_url":"https://api.github.com/repos/Toblerity/Fiona/comments{/number}","issue_comment_url":"https://api.github.com/repos/Toblerity/Fiona/issues/comments{/number}","contents_url":"https://api.github.com/repos/Toblerity/Fiona/contents/{+path}","compare_url":"https://api.github.com/repos/Toblerity/Fiona/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Toblerity/Fiona/merges","archive_url":"https://api.github.com/repos/Toblerity/Fiona/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Toblerity/Fiona/downloads","issues_url":"https://api.github.com/repos/Toblerity/Fiona/issues{/number}","pulls_url":"https://api.github.com/repos/Toblerity/Fiona/pulls{/number}","milestones_url":"https://api.github.com/repos/Toblerity/Fiona/milestones{/number}","notifications_url":"https://api.github.com/repos/Toblerity/Fiona/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Toblerity/Fiona/labels{/name}","releases_url":"https://api.github.com/repos/Toblerity/Fiona/releases{/id}","deployments_url":"https://api.github.com/repos/Toblerity/Fiona/deployments","created_at":"2011-12-31T19:47:00Z","updated_at":"2018-09-28T17:14:55Z","pushed_at":"2018-09-27T14:31:49Z","git_url":"git://github.com/Toblerity/Fiona.git","ssh_url":"git@github.com:Toblerity/Fiona.git","clone_url":"https://github.com/Toblerity/Fiona.git","svn_url":"https://github.com/Toblerity/Fiona","homepage":"http://toblerity.github.com/fiona/","size":2039,"stargazers_count":464,"watchers_count":464,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":100,"mirror_url":null,"archived":false,"open_issues_count":50,"license":{"key":"other","name":"NOASSERTION","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":100,"open_issues":50,"watchers":464,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Toblerity/Fiona/pulls/629"},"html":{"href":"https://github.com/Toblerity/Fiona/pull/629"},"issue":{"href":"https://api.github.com/repos/Toblerity/Fiona/issues/629"},"comments":{"href":"https://api.github.com/repos/Toblerity/Fiona/issues/629/comments"},"review_comments":{"href":"https://api.github.com/repos/Toblerity/Fiona/pulls/629/comments"},"review_comment":{"href":"https://api.github.com/repos/Toblerity/Fiona/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Toblerity/Fiona/pulls/629/commits"},"statuses":{"href":"https://api.github.com/repos/Toblerity/Fiona/statuses/8dd299db5e7a995aa13133c3c9db09cbcc804d85"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"snorfalorpagus"}}