{"action":"created","comment":{"url":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/comments/240014168","pull_request_review_id":182954415,"id":240014168,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDI0MDAxNDE2OA==","diff_hunk":"@@ -1100,3 +1029,286 @@ def __enter__(self):\n     def __exit__(self, exc_type, exc_val, exc_tb):\n         self.stop.set()\n         self.thread.join()\n+\n+\n+class AsyncRequests:\n+    \"\"\"Download a bunch of files in parallel\n+\n+    This is not really a class, more a name space encapsulating a bunch of calls.\n+    \"\"\"\n+    #: Identify ourselves\n+    USER_AGENT = \"bioconda/bioconda-utils\"\n+    #: Max connections to each server\n+    CONNECTIONS_PER_HOST = 4\n+\n+    @classmethod\n+    def fetch(cls, urls, descs, cb, datas):\n+        \"\"\"Fetch data from URLs.\n+\n+        This will use asyncio to manage a pool of connections at once, speeding\n+        up download as compared to iterative use of ``requests`` significantly.\n+        It will also retry on non-permanent HTTP error codes (i.e. 429, 502,\n+        503 and 504).\n+\n+        Args:\n+          urls: List of URLS\n+          descs: Matching list of descriptions (for progress display)\n+          cb: As each download is completed, data is passed through this function.\n+              Use to e.g. offload json parsing into download loop.\n+        \"\"\"\n+        try:\n+            loop = asyncio.get_event_loop()\n+        except RuntimeError:\n+            loop = asyncio.new_event_loop()\n+            asyncio.set_event_loop(loop)\n+\n+        task = asyncio.ensure_future(cls._async_fetch(urls, descs, cb, datas))\n+\n+        try:\n+            loop.run_until_complete(task)\n+        except KeyboardInterrupt:\n+            task.cancel()\n+            loop.run_forever()\n+            task.exception()\n+\n+        return task.result()\n+\n+    @classmethod\n+    async def _async_fetch(cls, urls, descs, cb, datas):\n+        conn = aiohttp.TCPConnector(limit_per_host=cls.CONNECTIONS_PER_HOST)\n+        async with aiohttp.ClientSession(\n+                connector=conn,\n+                headers={'User-Agent': cls.USER_AGENT}\n+        ) as session:\n+            coros = [\n+                asyncio.ensure_future(cls._async_fetch_one(session, url, desc, cb, data))\n+                for url, desc, data in zip(urls, descs, datas)\n+            ]\n+            with tqdm(asyncio.as_completed(coros),\n+                      total=len(coros),\n+                      desc=\"Downloading\", unit=\"files\") as t:\n+                result = [await coro for coro in t]\n+        return result\n+\n+    @staticmethod\n+    @backoff.on_exception(backoff.fibo, aiohttp.ClientResponseError, max_tries=20,\n+                          giveup=lambda ex: ex.code not in [429, 502, 503, 504])\n+    async def _async_fetch_one(session, url, desc, cb, data):\n+        result = []\n+        async with session.get(url) as resp:\n+            resp.raise_for_status()\n+            size = int(resp.headers.get(\"Content-Length\", 0))\n+            with tqdm(total=size, unit='B', unit_scale=True, unit_divisor=1024,\n+                      desc=desc, miniters=1,\n+                      disable=logger.getEffectiveLevel() > logging.INFO\n+            ) as progress:\n+                while True:\n+                    block = await resp.content.read(1024*16)\n+                    if not block:\n+                        break\n+                    progress.update(len(block))\n+                    result.append(block)\n+        if cb:\n+            return cb(b\"\".join(result), data)\n+        else:\n+            return b\"\".join(result)\n+\n+\n+class RepoData:\n+    \"\"\"Singleton providing access to package directory on anaconda cloud\n+\n+    If the first call provides a filename as **cache** argument, the\n+    file is used to cache the directory in CSV format.\n+\n+    Data structure:\n+\n+    Each **channel** hosted at anaconda cloud comprises a number of\n+    **subdirs** in which the individual package files reside. The\n+    **subdirs** can be one of **noarch**, **osx-64** and **linux-64**\n+    for Bioconda. (Technically ``(noarch|(linux|osx|win)-(64|32))``\n+    appears to be the schema).\n+\n+    For **channel/subdir** (aka **channel/platform**) combination, a\n+    **repodata.json** contains a **package** key describing each\n+    package file with at least the following information:\n+\n+    name: Package name (lowercase, alphanumeric + dash)\n+\n+    version: Version (no dash, PEP440)\n+\n+    build_number: Non negative integer indicating packaging revisions\n+\n+    build: String comprising hash of pinned dependencies and build\n+      number. Used to distinguish different builds of the same\n+      package/version combination.\n+\n+    depends: Runtime requirements for package as list of strings. We\n+      do not currently load this.\n+\n+    arch: Architecture key (x86_64). Not used by conda and not loaded\n+      here.\n+\n+    platform: Platform of package (osx, linux, noarch). Optional\n+      upstream, not used by conda. We generate this from the subdir\n+      information to have it available.\n+\n+\n+    Repodata versions:\n+\n+    The version is indicated by the key **repodata_version**, with\n+    absence of that key indication version 0.\n+\n+    In version 0, the **info** key contains the **subdir**,\n+    **platform**, **arch**, **default_python_version** and\n+    **default_numpy_version** keys. In version 1 it only contains the\n+    **subdir** key.\n+\n+    In version 1, a key **removed** was added, listing packages\n+    removed from the repository.\n+\n+    \"\"\"\n+\n+    REPODATA_URL = 'https://conda.anaconda.org/{channel}/{subdir}/repodata.json'\n+    REPODATA_LABELED_URL = 'https://conda.anaconda.org/{channel}/label/{label}/{subdir}/repodata.json'\n+    REPODATA_DEFAULTS_URL = 'https://repo.anaconda.com/pkgs/main/{subdir}/repodata.json'\n+\n+    _load_columns = ['build', 'build_number', 'name', 'version']\n+\n+    #: Columns available in internal dataframe\n+    columns = _load_columns + ['channel', 'subdir', 'platform']\n+    #: Platforms loaded\n+    platforms = ['linux', 'osx', 'noarch']\n+    #: Channels loaded\n+    channels = ['conda-forge', 'bioconda', 'defaults']\n+\n+    __instance = None\n+    def __new__(cls):\n+        \"\"\"Makes RepoData a singleton\"\"\"\n+        if RepoData.__instance is None:\n+            RepoData.__instance = object.__new__(cls)\n+        return RepoData.__instance\n+\n+    def __init__(self, cache=None):\n+        self.cache_file = cache\n+        self._df = None\n+\n+    @property\n+    def df(self):\n+        if self._df is None:\n+            self._df = self._load_channel_dataframe()\n+        return self._df\n+\n+    def _make_repodata_url(self, channel, platform):\n+        if channel == \"defaults\":\n+            # caveat: this only gets defaults main, not 'free', 'r' or 'pro'\n+            url_template = self.REPODATA_DEFAULTS_URL\n+        else:\n+            url_template = self.REPODATA_URL\n+\n+        url = url_template.format(channel=channel,\n+                                  subdir=self.platform2subdir(platform))\n+        return url\n+\n+    def _load_channel_dataframe(self):\n+        if self.cache_file is not None and os.path.exists(self.cache_file):\n+            logger.info(\"Loading repodata from cache %s\", self.cache_file)\n+            return pd.read_table(self.cache_file)\n+\n+        repos = list(product(self.channels, self.platforms))\n+        urls = [self._make_repodata_url(c, p) for c, p in repos]\n+        descs = [\"{}/{}\".format(c, p) for c, p in repos]\n+\n+        def to_dataframe(json_data, meta_data):\n+            channel, platform = meta_data\n+            repo = json.loads(json_data)\n+            df = pd.DataFrame.from_dict(repo['packages'], 'index',\n+                                        columns=self._load_columns)\n+            df['channel'] = channel\n+            df['platform'] = platform\n+            df['subdir'] = repo['info']['subdir']\n+            return df\n+\n+        dfs = AsyncRequests.fetch(urls, descs, to_dataframe, repos)\n+        res = pd.concat(dfs)\n+\n+        if self.cache_file is not None:\n+            res.to_csv(self.cache_file, sep='\\t')\n+\n+        return res\n+\n+    @staticmethod\n+    def native_platform():\n+        if sys.platform.startswith(\"linux\"):\n+            return \"linux\"\n+        if sys.platform.startswith(\"darwin\"):\n+            return \"osx\"\n+        raise ValueError(\"Running on unsupported platform\")\n+\n+    @staticmethod\n+    def platform2subdir(platform):\n+        if platform == 'linux':\n+            return 'linux-64'\n+        elif platform == 'osx':\n+            return 'osx-64'\n+        elif platform == 'noarch':\n+            return 'noarch'\n+        else:\n+            raise ValueError(\n+                'Unsupported platform: bioconda only supports linux, osx and noarch.')\n+\n+\n+\n+    def get_versions(self, name):\n+        \"\"\"Get versions available for package\n+\n+        Args:\n+          name: package name\n+\n+        Returns:\n+          Dictionary mapping version numbers to list of architectures\n+          e.g. {'0.1': ['linux'], '0.2': ['linux', 'osx'], '0.3': ['noarch']}\n+        \"\"\"\n+        # called from doc generator\n+        packages = self.df[self.df.name == name][['version', 'platform']]\n+        versions = packages.groupby('version').agg(lambda x: list(set(x)))\n+        return versions['platform'].to_dict()\n+\n+    def get_latest_versions(self, channel):\n+        \"\"\"Get the latest version for each package in **channel**\"\"\"\n+        # called from pypi module\n+        packages = self.df[self.df.channel == channel]['version']\n+        def max_vers(x):\n+            return max(VersionOrder(v) for v in x)\n+        vers = packages.groupby('name').agg(max_vers)\n+\n+    def get_package_data(self, key, channels=None, name=None, version=None,\n+                         build_number=None, platform=None, native=False):\n+        \"\"\"Get **key** for each package in **channels**\n+\n+        If **key** is a string, returns list of strings.\n+        If **key** is a list of string, returns tuple iterator.\n+        \"\"\"\n+        if native:\n+            platform = ['noarch', self.native_platform()]\n+\n+        if version is not None:\n+            version = str(version)\n+\n+        df = self.df\n+        for col, val in (\n+                ('name', name),\n+                ('channel', channels),\n+                ('version', version),\n+                ('build_number', build_number),\n+                ('platform', platform),\n+        ):\n+            if val is None:\n+                continue\n+            if isinstance(val, list) or isinstance(val, tuple):\n+                df = df[df[col].isin(val)]\n+            else:\n+                df = df[df[col] == val]\n+\n+        if isinstance(key, str):","path":"bioconda_utils/utils.py","position":567,"original_position":567,"commit_id":"0ae79e05ce978dabdb44c61d153392f5b2de7ee0","original_commit_id":"0ae79e05ce978dabdb44c61d153392f5b2de7ee0","user":{"login":"epruesse","id":2774474,"node_id":"MDQ6VXNlcjI3NzQ0NzQ=","avatar_url":"https://avatars3.githubusercontent.com/u/2774474?v=4","gravatar_id":"","url":"https://api.github.com/users/epruesse","html_url":"https://github.com/epruesse","followers_url":"https://api.github.com/users/epruesse/followers","following_url":"https://api.github.com/users/epruesse/following{/other_user}","gists_url":"https://api.github.com/users/epruesse/gists{/gist_id}","starred_url":"https://api.github.com/users/epruesse/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/epruesse/subscriptions","organizations_url":"https://api.github.com/users/epruesse/orgs","repos_url":"https://api.github.com/users/epruesse/repos","events_url":"https://api.github.com/users/epruesse/events{/privacy}","received_events_url":"https://api.github.com/users/epruesse/received_events","type":"User","site_admin":false},"body":"Yes ... perhaps. `check_channel_recipes` counts the number of packages for each subdir matching name, version, build_number in the top two channels (so CF and us) to determine whether a recipe can be skipped. I'm not positive I follow the logic there, so I left it identical (hoping for no bugs) to the original behavior.","created_at":"2018-12-08T19:30:10Z","updated_at":"2018-12-08T19:30:11Z","html_url":"https://github.com/bioconda/bioconda-utils/pull/361#discussion_r240014168","pull_request_url":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/361","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/comments/240014168"},"html":{"href":"https://github.com/bioconda/bioconda-utils/pull/361#discussion_r240014168"},"pull_request":{"href":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/361"}},"in_reply_to_id":240001006},"pull_request":{"url":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/361","id":235169656,"node_id":"MDExOlB1bGxSZXF1ZXN0MjM1MTY5NjU2","html_url":"https://github.com/bioconda/bioconda-utils/pull/361","diff_url":"https://github.com/bioconda/bioconda-utils/pull/361.diff","patch_url":"https://github.com/bioconda/bioconda-utils/pull/361.patch","issue_url":"https://api.github.com/repos/bioconda/bioconda-utils/issues/361","number":361,"state":"open","locked":false,"title":"Merge various ways of accessing anaconda repodata","user":{"login":"epruesse","id":2774474,"node_id":"MDQ6VXNlcjI3NzQ0NzQ=","avatar_url":"https://avatars3.githubusercontent.com/u/2774474?v=4","gravatar_id":"","url":"https://api.github.com/users/epruesse","html_url":"https://github.com/epruesse","followers_url":"https://api.github.com/users/epruesse/followers","following_url":"https://api.github.com/users/epruesse/following{/other_user}","gists_url":"https://api.github.com/users/epruesse/gists{/gist_id}","starred_url":"https://api.github.com/users/epruesse/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/epruesse/subscriptions","organizations_url":"https://api.github.com/users/epruesse/orgs","repos_url":"https://api.github.com/users/epruesse/repos","events_url":"https://api.github.com/users/epruesse/events{/privacy}","received_events_url":"https://api.github.com/users/epruesse/received_events","type":"User","site_admin":false},"body":"Checking anaconda cloud for available packages has become cluttered, with a jumble of functions doing similar work in all manner of interesting ways.\r\n\r\nThis collects those functions into a singleton class `RepoData`, which itself uses the `channel_dataframe()` approach, but encapsulating access to that DataFrame within the class.\r\n\r\nTotal code size is larger than before because of some extra features. I'm now using `aiohttp` instead of `requests` so that the 9 repodata.json files can be fetched in parallel (or piped through the same already ramped up TCP connection). Loading the whole thing now takes about 6s for me.\r\n\r\n## Details:\r\n\r\n#### Requirements:\r\n - Bump to pandas 0.23 for improvements to `from_dict()`\r\n\r\n#### Lint\r\n - Use RepoData  instead of passing a DataFrame from various locations into lint.\r\n - Use get_package_data instead of messing with DataFrame directly\r\n - Remove unused bump_build_number\r\n\r\n#### Bioconductor Skeleton\r\n - Use RepoData instead of parsing json from `get_channel_repodata()` directly\r\n\r\n#### Build\r\n - Lint no longer needs `channel_dataframe`\r\n - Pass only channels to be checked on to `get_package_paths`\r\n\r\n#### Cli\r\n - Rewrote `duplicates` command (broken since change to channel order)\r\n - Lint no longer needs `channel_dataframe` (caching preserved though)\r\n\r\n#### PyPi\r\n - Use get_latest_versions instead of messing with DataFrame directly\r\n\r\n#### generate_docs\r\n - Moved RepoData class to utils (and extended heavily)\r\n\r\n#### General\r\n - include `noarch` packages in the dataframe\r\n - only load `noarch` once (i.e. load `noarch`, `linux` and `osx`)\r\n - parses both version 0 and 1 of repodata.json format ","created_at":"2018-12-01T04:40:11Z","updated_at":"2018-12-08T19:30:11Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b3ad1c9e88796dc825f4674593c6979fcf8cb4e8","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/361/commits","review_comments_url":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/361/comments","review_comment_url":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/comments{/number}","comments_url":"https://api.github.com/repos/bioconda/bioconda-utils/issues/361/comments","statuses_url":"https://api.github.com/repos/bioconda/bioconda-utils/statuses/0ae79e05ce978dabdb44c61d153392f5b2de7ee0","head":{"label":"epruesse:channel_data","ref":"channel_data","sha":"0ae79e05ce978dabdb44c61d153392f5b2de7ee0","user":{"login":"epruesse","id":2774474,"node_id":"MDQ6VXNlcjI3NzQ0NzQ=","avatar_url":"https://avatars3.githubusercontent.com/u/2774474?v=4","gravatar_id":"","url":"https://api.github.com/users/epruesse","html_url":"https://github.com/epruesse","followers_url":"https://api.github.com/users/epruesse/followers","following_url":"https://api.github.com/users/epruesse/following{/other_user}","gists_url":"https://api.github.com/users/epruesse/gists{/gist_id}","starred_url":"https://api.github.com/users/epruesse/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/epruesse/subscriptions","organizations_url":"https://api.github.com/users/epruesse/orgs","repos_url":"https://api.github.com/users/epruesse/repos","events_url":"https://api.github.com/users/epruesse/events{/privacy}","received_events_url":"https://api.github.com/users/epruesse/received_events","type":"User","site_admin":false},"repo":{"id":100755281,"node_id":"MDEwOlJlcG9zaXRvcnkxMDA3NTUyODE=","name":"bioconda-utils","full_name":"epruesse/bioconda-utils","private":false,"owner":{"login":"epruesse","id":2774474,"node_id":"MDQ6VXNlcjI3NzQ0NzQ=","avatar_url":"https://avatars3.githubusercontent.com/u/2774474?v=4","gravatar_id":"","url":"https://api.github.com/users/epruesse","html_url":"https://github.com/epruesse","followers_url":"https://api.github.com/users/epruesse/followers","following_url":"https://api.github.com/users/epruesse/following{/other_user}","gists_url":"https://api.github.com/users/epruesse/gists{/gist_id}","starred_url":"https://api.github.com/users/epruesse/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/epruesse/subscriptions","organizations_url":"https://api.github.com/users/epruesse/orgs","repos_url":"https://api.github.com/users/epruesse/repos","events_url":"https://api.github.com/users/epruesse/events{/privacy}","received_events_url":"https://api.github.com/users/epruesse/received_events","type":"User","site_admin":false},"html_url":"https://github.com/epruesse/bioconda-utils","description":"Utilities for building and managing bioconda recipes","fork":true,"url":"https://api.github.com/repos/epruesse/bioconda-utils","forks_url":"https://api.github.com/repos/epruesse/bioconda-utils/forks","keys_url":"https://api.github.com/repos/epruesse/bioconda-utils/keys{/key_id}","collaborators_url":"https://api.github.com/repos/epruesse/bioconda-utils/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/epruesse/bioconda-utils/teams","hooks_url":"https://api.github.com/repos/epruesse/bioconda-utils/hooks","issue_events_url":"https://api.github.com/repos/epruesse/bioconda-utils/issues/events{/number}","events_url":"https://api.github.com/repos/epruesse/bioconda-utils/events","assignees_url":"https://api.github.com/repos/epruesse/bioconda-utils/assignees{/user}","branches_url":"https://api.github.com/repos/epruesse/bioconda-utils/branches{/branch}","tags_url":"https://api.github.com/repos/epruesse/bioconda-utils/tags","blobs_url":"https://api.github.com/repos/epruesse/bioconda-utils/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/epruesse/bioconda-utils/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/epruesse/bioconda-utils/git/refs{/sha}","trees_url":"https://api.github.com/repos/epruesse/bioconda-utils/git/trees{/sha}","statuses_url":"https://api.github.com/repos/epruesse/bioconda-utils/statuses/{sha}","languages_url":"https://api.github.com/repos/epruesse/bioconda-utils/languages","stargazers_url":"https://api.github.com/repos/epruesse/bioconda-utils/stargazers","contributors_url":"https://api.github.com/repos/epruesse/bioconda-utils/contributors","subscribers_url":"https://api.github.com/repos/epruesse/bioconda-utils/subscribers","subscription_url":"https://api.github.com/repos/epruesse/bioconda-utils/subscription","commits_url":"https://api.github.com/repos/epruesse/bioconda-utils/commits{/sha}","git_commits_url":"https://api.github.com/repos/epruesse/bioconda-utils/git/commits{/sha}","comments_url":"https://api.github.com/repos/epruesse/bioconda-utils/comments{/number}","issue_comment_url":"https://api.github.com/repos/epruesse/bioconda-utils/issues/comments{/number}","contents_url":"https://api.github.com/repos/epruesse/bioconda-utils/contents/{+path}","compare_url":"https://api.github.com/repos/epruesse/bioconda-utils/compare/{base}...{head}","merges_url":"https://api.github.com/repos/epruesse/bioconda-utils/merges","archive_url":"https://api.github.com/repos/epruesse/bioconda-utils/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/epruesse/bioconda-utils/downloads","issues_url":"https://api.github.com/repos/epruesse/bioconda-utils/issues{/number}","pulls_url":"https://api.github.com/repos/epruesse/bioconda-utils/pulls{/number}","milestones_url":"https://api.github.com/repos/epruesse/bioconda-utils/milestones{/number}","notifications_url":"https://api.github.com/repos/epruesse/bioconda-utils/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/epruesse/bioconda-utils/labels{/name}","releases_url":"https://api.github.com/repos/epruesse/bioconda-utils/releases{/id}","deployments_url":"https://api.github.com/repos/epruesse/bioconda-utils/deployments","created_at":"2017-08-18T22:42:03Z","updated_at":"2017-08-18T22:42:05Z","pushed_at":"2018-12-08T01:02:39Z","git_url":"git://github.com/epruesse/bioconda-utils.git","ssh_url":"git@github.com:epruesse/bioconda-utils.git","clone_url":"https://github.com/epruesse/bioconda-utils.git","svn_url":"https://github.com/epruesse/bioconda-utils","homepage":null,"size":6988,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"bioconda:master","ref":"master","sha":"30d01deb07c8247a86dd1356ed9250ea07302cd9","user":{"login":"bioconda","id":14253259,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE0MjUzMjU5","avatar_url":"https://avatars3.githubusercontent.com/u/14253259?v=4","gravatar_id":"","url":"https://api.github.com/users/bioconda","html_url":"https://github.com/bioconda","followers_url":"https://api.github.com/users/bioconda/followers","following_url":"https://api.github.com/users/bioconda/following{/other_user}","gists_url":"https://api.github.com/users/bioconda/gists{/gist_id}","starred_url":"https://api.github.com/users/bioconda/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bioconda/subscriptions","organizations_url":"https://api.github.com/users/bioconda/orgs","repos_url":"https://api.github.com/users/bioconda/repos","events_url":"https://api.github.com/users/bioconda/events{/privacy}","received_events_url":"https://api.github.com/users/bioconda/received_events","type":"Organization","site_admin":false},"repo":{"id":59689137,"node_id":"MDEwOlJlcG9zaXRvcnk1OTY4OTEzNw==","name":"bioconda-utils","full_name":"bioconda/bioconda-utils","private":false,"owner":{"login":"bioconda","id":14253259,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE0MjUzMjU5","avatar_url":"https://avatars3.githubusercontent.com/u/14253259?v=4","gravatar_id":"","url":"https://api.github.com/users/bioconda","html_url":"https://github.com/bioconda","followers_url":"https://api.github.com/users/bioconda/followers","following_url":"https://api.github.com/users/bioconda/following{/other_user}","gists_url":"https://api.github.com/users/bioconda/gists{/gist_id}","starred_url":"https://api.github.com/users/bioconda/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bioconda/subscriptions","organizations_url":"https://api.github.com/users/bioconda/orgs","repos_url":"https://api.github.com/users/bioconda/repos","events_url":"https://api.github.com/users/bioconda/events{/privacy}","received_events_url":"https://api.github.com/users/bioconda/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/bioconda/bioconda-utils","description":"Utilities for building and managing bioconda recipes","fork":false,"url":"https://api.github.com/repos/bioconda/bioconda-utils","forks_url":"https://api.github.com/repos/bioconda/bioconda-utils/forks","keys_url":"https://api.github.com/repos/bioconda/bioconda-utils/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bioconda/bioconda-utils/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bioconda/bioconda-utils/teams","hooks_url":"https://api.github.com/repos/bioconda/bioconda-utils/hooks","issue_events_url":"https://api.github.com/repos/bioconda/bioconda-utils/issues/events{/number}","events_url":"https://api.github.com/repos/bioconda/bioconda-utils/events","assignees_url":"https://api.github.com/repos/bioconda/bioconda-utils/assignees{/user}","branches_url":"https://api.github.com/repos/bioconda/bioconda-utils/branches{/branch}","tags_url":"https://api.github.com/repos/bioconda/bioconda-utils/tags","blobs_url":"https://api.github.com/repos/bioconda/bioconda-utils/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bioconda/bioconda-utils/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bioconda/bioconda-utils/git/refs{/sha}","trees_url":"https://api.github.com/repos/bioconda/bioconda-utils/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bioconda/bioconda-utils/statuses/{sha}","languages_url":"https://api.github.com/repos/bioconda/bioconda-utils/languages","stargazers_url":"https://api.github.com/repos/bioconda/bioconda-utils/stargazers","contributors_url":"https://api.github.com/repos/bioconda/bioconda-utils/contributors","subscribers_url":"https://api.github.com/repos/bioconda/bioconda-utils/subscribers","subscription_url":"https://api.github.com/repos/bioconda/bioconda-utils/subscription","commits_url":"https://api.github.com/repos/bioconda/bioconda-utils/commits{/sha}","git_commits_url":"https://api.github.com/repos/bioconda/bioconda-utils/git/commits{/sha}","comments_url":"https://api.github.com/repos/bioconda/bioconda-utils/comments{/number}","issue_comment_url":"https://api.github.com/repos/bioconda/bioconda-utils/issues/comments{/number}","contents_url":"https://api.github.com/repos/bioconda/bioconda-utils/contents/{+path}","compare_url":"https://api.github.com/repos/bioconda/bioconda-utils/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bioconda/bioconda-utils/merges","archive_url":"https://api.github.com/repos/bioconda/bioconda-utils/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bioconda/bioconda-utils/downloads","issues_url":"https://api.github.com/repos/bioconda/bioconda-utils/issues{/number}","pulls_url":"https://api.github.com/repos/bioconda/bioconda-utils/pulls{/number}","milestones_url":"https://api.github.com/repos/bioconda/bioconda-utils/milestones{/number}","notifications_url":"https://api.github.com/repos/bioconda/bioconda-utils/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bioconda/bioconda-utils/labels{/name}","releases_url":"https://api.github.com/repos/bioconda/bioconda-utils/releases{/id}","deployments_url":"https://api.github.com/repos/bioconda/bioconda-utils/deployments","created_at":"2016-05-25T18:47:03Z","updated_at":"2018-12-07T22:45:38Z","pushed_at":"2018-12-08T15:22:49Z","git_url":"git://github.com/bioconda/bioconda-utils.git","ssh_url":"git@github.com:bioconda/bioconda-utils.git","clone_url":"https://github.com/bioconda/bioconda-utils.git","svn_url":"https://github.com/bioconda/bioconda-utils","homepage":null,"size":6749,"stargazers_count":34,"watchers_count":34,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":41,"mirror_url":null,"archived":false,"open_issues_count":64,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":41,"open_issues":64,"watchers":34,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/361"},"html":{"href":"https://github.com/bioconda/bioconda-utils/pull/361"},"issue":{"href":"https://api.github.com/repos/bioconda/bioconda-utils/issues/361"},"comments":{"href":"https://api.github.com/repos/bioconda/bioconda-utils/issues/361/comments"},"review_comments":{"href":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/361/comments"},"review_comment":{"href":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/bioconda/bioconda-utils/pulls/361/commits"},"statuses":{"href":"https://api.github.com/repos/bioconda/bioconda-utils/statuses/0ae79e05ce978dabdb44c61d153392f5b2de7ee0"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"epruesse"}}