{"action":"created","comment":{"url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/comments/48465080","id":48465080,"diff_hunk":"@@ -0,0 +1,247 @@\n+import logging\n+import os\n+import shutil\n+\n+from django.conf import settings\n+\n+from readthedocs.builds.constants import LATEST\n+from readthedocs.doc_builder.config import ConfigWrapper\n+from readthedocs.doc_builder.loader import get_builder_class\n+from readthedocs.projects.constants import LOG_TEMPLATE\n+\n+log = logging.getLogger(__name__)\n+\n+\n+class PythonEnvironment(object):\n+\n+    def __init__(self, version, build_env, config=None):\n+        self.version = version\n+        self.project = version.project\n+        self.build_env = build_env\n+        if config:\n+            self.config = config\n+        else:\n+            self.config = ConfigWrapper(version=version, yaml_config={})\n+        # Compute here, since it's used a lot\n+        self.checkout_path = self.project.checkout_path(self.version.slug)\n+\n+    def _log(self, msg):\n+        log.info(LOG_TEMPLATE\n+                 .format(project=self.project.slug,\n+                         version=self.version.slug,\n+                         msg=msg))\n+\n+    def delete_existing_build_dir(self):\n+\n+        # Handle deleting old build dir\n+        build_dir = os.path.join(\n+            self.venv_path(version=self.version.slug),\n+            'build')\n+        if os.path.exists(build_dir):\n+            self._log('Removing existing build directory')\n+            shutil.rmtree(build_dir)\n+\n+    def install_package(self):\n+        setup_path = os.path.join(self.checkout_path, 'setup.py')\n+        if os.path.isfile(setup_path) and self.config.install_project:\n+            if getattr(settings, 'USE_PIP_INSTALL', False):\n+                self.build_env.run(\n+                    'python',\n+                    self.venv_bin(version=self.version.slug, filename='pip'),\n+                    'install',\n+                    '--ignore-installed',\n+                    '--cache-dir',\n+                    self.project.pip_cache_path,\n+                    '.',\n+                    cwd=self.checkout_path,\n+                    bin_path=self.venv_bin(version=self.version.slug)\n+                )\n+            else:\n+                self.build_env.run(\n+                    'python',\n+                    'setup.py',\n+                    'install',\n+                    '--force',\n+                    cwd=self.checkout_path,\n+                    bin_path=self.venv_bin(version=self.version.slug)\n+                )\n+\n+    def venv_bin(self, version=LATEST, filename=None):\n+        \"\"\"Return path to the virtualenv bin path, or a specific binary\n+\n+        :param version: Version slug to use in path name\n+        :param filename: If specified, add this filename to the path return\n+        :returns: Path to virtualenv bin or filename in virtualenv bin\n+        \"\"\"\n+        parts = [self.venv_path(version), 'bin']\n+        if filename is not None:\n+            parts.append(filename)\n+        return os.path.join(*parts)\n+\n+\n+class Virtualenv(PythonEnvironment):\n+\n+    def venv_path(self, version=LATEST):\n+        return os.path.join(self.project.doc_path, 'envs', version)\n+\n+    def setup_base(self):\n+        site_packages = '--no-site-packages'\n+        if self.config.use_system_site_packages:\n+            site_packages = '--system-site-packages'\n+        env_path = self.venv_path(version=self.version.slug)\n+        self.build_env.run(\n+            self.config.python_interpreter,\n+            '-mvirtualenv',\n+            site_packages,\n+            env_path,\n+        )\n+\n+    def install_core_requirements(self):\n+        requirements = [\n+            'sphinx==1.3.1',\n+            'Pygments==2.0.2',\n+            'setuptools==18.6.1',\n+            'docutils==0.11',\n+            'mkdocs==0.14.0',\n+            'mock==1.0.1',\n+            'pillow==2.6.1',\n+            'readthedocs-sphinx-ext==0.5.4',\n+            'sphinx-rtd-theme==0.1.9',\n+            'alabaster>=0.7,<0.8,!=0.7.5',\n+            'recommonmark==0.1.1',\n+        ]\n+\n+        cmd = [\n+            'python',\n+            self.venv_bin(version=self.version.slug, filename='pip'),\n+            'install',\n+            '--use-wheel',\n+            '-U',\n+            '--cache-dir',\n+            self.project.pip_cache_path,\n+        ]\n+        if self.config.use_system_site_packages:\n+            # Other code expects sphinx-build to be installed inside the\n+            # virtualenv.  Using the -I option makes sure it gets installed\n+            # even if it is already installed system-wide (and\n+            # --system-site-packages is used)\n+            cmd.append('-I')\n+        cmd.extend(requirements)\n+        self.build_env.run(\n+            *cmd,\n+            bin_path=self.venv_bin(version=self.version.slug)\n+        )\n+\n+    def install_user_requirements(self):\n+        requirements_file_path = self.config.requirements_file\n+        if not requirements_file_path:\n+            builder_class = get_builder_class(self.project.documentation_type)\n+            docs_dir = (builder_class(build_env=self.build_env, python_env=self)\n+                        .docs_dir())\n+            for path in [docs_dir, '']:\n+                for req_file in ['pip_requirements.txt', 'requirements.txt']:\n+                    test_path = os.path.join(self.checkout_path, path, req_file)\n+                    if os.path.exists(test_path):\n+                        requirements_file_path = test_path\n+                        break\n+\n+        if requirements_file_path:\n+            self.build_env.run(\n+                'python',\n+                self.venv_bin(version=self.version.slug, filename='pip'),\n+                'install',\n+                '--exists-action=w',\n+                '--cache-dir',\n+                self.project.pip_cache_path,\n+                '-r{0}'.format(requirements_file_path),\n+                cwd=self.checkout_path,\n+                bin_path=self.venv_bin(version=self.version.slug)\n+            )\n+\n+\n+class Conda(PythonEnvironment):\n+\n+    def venv_path(self, version=LATEST):\n+        return os.path.join(self.project.doc_path, 'conda', version)\n+\n+    def setup_base(self):\n+        conda_env_path = os.path.join(self.project.doc_path, 'conda')\n+        version_path = os.path.join(conda_env_path, self.version.slug)\n+\n+        if os.path.exists(version_path):\n+            # Re-create conda directory each time to keep fresh state\n+            self._log('Removing existing conda directory')\n+            shutil.rmtree(version_path)\n+        self.build_env.run(\n+            'conda',\n+            'create',\n+            '--yes',\n+            '--name',\n+            self.version.slug,\n+            'python={python_version}'.format(python_version=self.config.python_version),\n+            environment={'CONDA_ENVS_PATH': conda_env_path}\n+        )\n+\n+    def install_core_requirements(self):\n+        conda_env_path = os.path.join(self.project.doc_path, 'conda')\n+\n+        # Use conda for requirements it packages\n+        requirements = [\n+            'sphinx==1.3.1',\n+            'Pygments==2.0.2',\n+            'docutils==0.11',\n+            # 'mkdocs==0.14.0',\n+            'mock==1.0.1',\n+            'pillow==3.0.0',\n+            # 'readthedocs-sphinx-ext==0.5.4',\n+            'sphinx_rtd_theme==0.1.7',\n+            'alabaster>=0.7,<0.8,!=0.7.5',\n+            # 'recommonmark==0.1.1',","path":"readthedocs/doc_builder/python_environments.py","position":199,"original_position":199,"commit_id":"aaffe6effd166596ce738c1403bd9745262f9ea5","original_commit_id":"aaffe6effd166596ce738c1403bd9745262f9ea5","user":{"login":"agjohnson","id":1140183,"avatar_url":"https://avatars.githubusercontent.com/u/1140183?v=3","gravatar_id":"","url":"https://api.github.com/users/agjohnson","html_url":"https://github.com/agjohnson","followers_url":"https://api.github.com/users/agjohnson/followers","following_url":"https://api.github.com/users/agjohnson/following{/other_user}","gists_url":"https://api.github.com/users/agjohnson/gists{/gist_id}","starred_url":"https://api.github.com/users/agjohnson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/agjohnson/subscriptions","organizations_url":"https://api.github.com/users/agjohnson/orgs","repos_url":"https://api.github.com/users/agjohnson/repos","events_url":"https://api.github.com/users/agjohnson/events{/privacy}","received_events_url":"https://api.github.com/users/agjohnson/received_events","type":"User","site_admin":false},"body":"Can these commented packages be removed here? Ditto for the `Virtualenv` class for consistency","created_at":"2015-12-28T06:54:39Z","updated_at":"2015-12-28T06:54:39Z","html_url":"https://github.com/rtfd/readthedocs.org/pull/1849#discussion_r48465080","pull_request_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1849","_links":{"self":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/comments/48465080"},"html":{"href":"https://github.com/rtfd/readthedocs.org/pull/1849#discussion_r48465080"},"pull_request":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1849"}}},"pull_request":{"url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1849","id":52610062,"html_url":"https://github.com/rtfd/readthedocs.org/pull/1849","diff_url":"https://github.com/rtfd/readthedocs.org/pull/1849.diff","patch_url":"https://github.com/rtfd/readthedocs.org/pull/1849.patch","issue_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/1849","number":1849,"state":"open","locked":false,"title":"Add support for Conda as a build environment","user":{"login":"ericholscher","id":25510,"avatar_url":"https://avatars.githubusercontent.com/u/25510?v=3","gravatar_id":"","url":"https://api.github.com/users/ericholscher","html_url":"https://github.com/ericholscher","followers_url":"https://api.github.com/users/ericholscher/followers","following_url":"https://api.github.com/users/ericholscher/following{/other_user}","gists_url":"https://api.github.com/users/ericholscher/gists{/gist_id}","starred_url":"https://api.github.com/users/ericholscher/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ericholscher/subscriptions","organizations_url":"https://api.github.com/users/ericholscher/orgs","repos_url":"https://api.github.com/users/ericholscher/repos","events_url":"https://api.github.com/users/ericholscher/events{/privacy}","received_events_url":"https://api.github.com/users/ericholscher/received_events","type":"User","site_admin":false},"body":"This is a work in progress that adds support for specifying a conda [Environment File](http://conda.pydata.org/docs/using/envs.html#export-the-environment-file) for your documentation build. \r\n\r\nIt still needs the following:\r\n\r\n- [x] Tests\r\n- [x] More specific documentation (especially around limitations and usage)","created_at":"2015-12-03T23:39:08Z","updated_at":"2015-12-28T06:54:39Z","closed_at":null,"merged_at":null,"merge_commit_sha":"cd0bb22503662b89f94579b1e1ea4415a90dbeba","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1849/commits","review_comments_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1849/comments","review_comment_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/1849/comments","statuses_url":"https://api.github.com/repos/rtfd/readthedocs.org/statuses/aaffe6effd166596ce738c1403bd9745262f9ea5","head":{"label":"rtfd:conda-support","ref":"conda-support","sha":"aaffe6effd166596ce738c1403bd9745262f9ea5","user":{"login":"rtfd","id":366329,"avatar_url":"https://avatars.githubusercontent.com/u/366329?v=3","gravatar_id":"","url":"https://api.github.com/users/rtfd","html_url":"https://github.com/rtfd","followers_url":"https://api.github.com/users/rtfd/followers","following_url":"https://api.github.com/users/rtfd/following{/other_user}","gists_url":"https://api.github.com/users/rtfd/gists{/gist_id}","starred_url":"https://api.github.com/users/rtfd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rtfd/subscriptions","organizations_url":"https://api.github.com/users/rtfd/orgs","repos_url":"https://api.github.com/users/rtfd/repos","events_url":"https://api.github.com/users/rtfd/events{/privacy}","received_events_url":"https://api.github.com/users/rtfd/received_events","type":"Organization","site_admin":false},"repo":{"id":841835,"name":"readthedocs.org","full_name":"rtfd/readthedocs.org","owner":{"login":"rtfd","id":366329,"avatar_url":"https://avatars.githubusercontent.com/u/366329?v=3","gravatar_id":"","url":"https://api.github.com/users/rtfd","html_url":"https://github.com/rtfd","followers_url":"https://api.github.com/users/rtfd/followers","following_url":"https://api.github.com/users/rtfd/following{/other_user}","gists_url":"https://api.github.com/users/rtfd/gists{/gist_id}","starred_url":"https://api.github.com/users/rtfd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rtfd/subscriptions","organizations_url":"https://api.github.com/users/rtfd/orgs","repos_url":"https://api.github.com/users/rtfd/repos","events_url":"https://api.github.com/users/rtfd/events{/privacy}","received_events_url":"https://api.github.com/users/rtfd/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rtfd/readthedocs.org","description":"source code to readthedocs.org","fork":false,"url":"https://api.github.com/repos/rtfd/readthedocs.org","forks_url":"https://api.github.com/repos/rtfd/readthedocs.org/forks","keys_url":"https://api.github.com/repos/rtfd/readthedocs.org/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rtfd/readthedocs.org/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rtfd/readthedocs.org/teams","hooks_url":"https://api.github.com/repos/rtfd/readthedocs.org/hooks","issue_events_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/events{/number}","events_url":"https://api.github.com/repos/rtfd/readthedocs.org/events","assignees_url":"https://api.github.com/repos/rtfd/readthedocs.org/assignees{/user}","branches_url":"https://api.github.com/repos/rtfd/readthedocs.org/branches{/branch}","tags_url":"https://api.github.com/repos/rtfd/readthedocs.org/tags","blobs_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/refs{/sha}","trees_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rtfd/readthedocs.org/statuses/{sha}","languages_url":"https://api.github.com/repos/rtfd/readthedocs.org/languages","stargazers_url":"https://api.github.com/repos/rtfd/readthedocs.org/stargazers","contributors_url":"https://api.github.com/repos/rtfd/readthedocs.org/contributors","subscribers_url":"https://api.github.com/repos/rtfd/readthedocs.org/subscribers","subscription_url":"https://api.github.com/repos/rtfd/readthedocs.org/subscription","commits_url":"https://api.github.com/repos/rtfd/readthedocs.org/commits{/sha}","git_commits_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/commits{/sha}","comments_url":"https://api.github.com/repos/rtfd/readthedocs.org/comments{/number}","issue_comment_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/comments{/number}","contents_url":"https://api.github.com/repos/rtfd/readthedocs.org/contents/{+path}","compare_url":"https://api.github.com/repos/rtfd/readthedocs.org/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rtfd/readthedocs.org/merges","archive_url":"https://api.github.com/repos/rtfd/readthedocs.org/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rtfd/readthedocs.org/downloads","issues_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues{/number}","pulls_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls{/number}","milestones_url":"https://api.github.com/repos/rtfd/readthedocs.org/milestones{/number}","notifications_url":"https://api.github.com/repos/rtfd/readthedocs.org/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rtfd/readthedocs.org/labels{/name}","releases_url":"https://api.github.com/repos/rtfd/readthedocs.org/releases{/id}","created_at":"2010-08-16T19:18:06Z","updated_at":"2015-12-27T20:58:40Z","pushed_at":"2015-12-27T22:47:37Z","git_url":"git://github.com/rtfd/readthedocs.org.git","ssh_url":"git@github.com:rtfd/readthedocs.org.git","clone_url":"https://github.com/rtfd/readthedocs.org.git","svn_url":"https://github.com/rtfd/readthedocs.org","homepage":"http://rtfd.org/","size":36038,"stargazers_count":2313,"watchers_count":2313,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1029,"mirror_url":null,"open_issues_count":212,"forks":1029,"open_issues":212,"watchers":2313,"default_branch":"master"}},"base":{"label":"rtfd:master","ref":"master","sha":"c6db8701986e8eb075b92916067e5904cd13fe9f","user":{"login":"rtfd","id":366329,"avatar_url":"https://avatars.githubusercontent.com/u/366329?v=3","gravatar_id":"","url":"https://api.github.com/users/rtfd","html_url":"https://github.com/rtfd","followers_url":"https://api.github.com/users/rtfd/followers","following_url":"https://api.github.com/users/rtfd/following{/other_user}","gists_url":"https://api.github.com/users/rtfd/gists{/gist_id}","starred_url":"https://api.github.com/users/rtfd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rtfd/subscriptions","organizations_url":"https://api.github.com/users/rtfd/orgs","repos_url":"https://api.github.com/users/rtfd/repos","events_url":"https://api.github.com/users/rtfd/events{/privacy}","received_events_url":"https://api.github.com/users/rtfd/received_events","type":"Organization","site_admin":false},"repo":{"id":841835,"name":"readthedocs.org","full_name":"rtfd/readthedocs.org","owner":{"login":"rtfd","id":366329,"avatar_url":"https://avatars.githubusercontent.com/u/366329?v=3","gravatar_id":"","url":"https://api.github.com/users/rtfd","html_url":"https://github.com/rtfd","followers_url":"https://api.github.com/users/rtfd/followers","following_url":"https://api.github.com/users/rtfd/following{/other_user}","gists_url":"https://api.github.com/users/rtfd/gists{/gist_id}","starred_url":"https://api.github.com/users/rtfd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rtfd/subscriptions","organizations_url":"https://api.github.com/users/rtfd/orgs","repos_url":"https://api.github.com/users/rtfd/repos","events_url":"https://api.github.com/users/rtfd/events{/privacy}","received_events_url":"https://api.github.com/users/rtfd/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rtfd/readthedocs.org","description":"source code to readthedocs.org","fork":false,"url":"https://api.github.com/repos/rtfd/readthedocs.org","forks_url":"https://api.github.com/repos/rtfd/readthedocs.org/forks","keys_url":"https://api.github.com/repos/rtfd/readthedocs.org/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rtfd/readthedocs.org/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rtfd/readthedocs.org/teams","hooks_url":"https://api.github.com/repos/rtfd/readthedocs.org/hooks","issue_events_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/events{/number}","events_url":"https://api.github.com/repos/rtfd/readthedocs.org/events","assignees_url":"https://api.github.com/repos/rtfd/readthedocs.org/assignees{/user}","branches_url":"https://api.github.com/repos/rtfd/readthedocs.org/branches{/branch}","tags_url":"https://api.github.com/repos/rtfd/readthedocs.org/tags","blobs_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/refs{/sha}","trees_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rtfd/readthedocs.org/statuses/{sha}","languages_url":"https://api.github.com/repos/rtfd/readthedocs.org/languages","stargazers_url":"https://api.github.com/repos/rtfd/readthedocs.org/stargazers","contributors_url":"https://api.github.com/repos/rtfd/readthedocs.org/contributors","subscribers_url":"https://api.github.com/repos/rtfd/readthedocs.org/subscribers","subscription_url":"https://api.github.com/repos/rtfd/readthedocs.org/subscription","commits_url":"https://api.github.com/repos/rtfd/readthedocs.org/commits{/sha}","git_commits_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/commits{/sha}","comments_url":"https://api.github.com/repos/rtfd/readthedocs.org/comments{/number}","issue_comment_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/comments{/number}","contents_url":"https://api.github.com/repos/rtfd/readthedocs.org/contents/{+path}","compare_url":"https://api.github.com/repos/rtfd/readthedocs.org/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rtfd/readthedocs.org/merges","archive_url":"https://api.github.com/repos/rtfd/readthedocs.org/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rtfd/readthedocs.org/downloads","issues_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues{/number}","pulls_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls{/number}","milestones_url":"https://api.github.com/repos/rtfd/readthedocs.org/milestones{/number}","notifications_url":"https://api.github.com/repos/rtfd/readthedocs.org/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rtfd/readthedocs.org/labels{/name}","releases_url":"https://api.github.com/repos/rtfd/readthedocs.org/releases{/id}","created_at":"2010-08-16T19:18:06Z","updated_at":"2015-12-27T20:58:40Z","pushed_at":"2015-12-27T22:47:37Z","git_url":"git://github.com/rtfd/readthedocs.org.git","ssh_url":"git@github.com:rtfd/readthedocs.org.git","clone_url":"https://github.com/rtfd/readthedocs.org.git","svn_url":"https://github.com/rtfd/readthedocs.org","homepage":"http://rtfd.org/","size":36038,"stargazers_count":2313,"watchers_count":2313,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1029,"mirror_url":null,"open_issues_count":212,"forks":1029,"open_issues":212,"watchers":2313,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1849"},"html":{"href":"https://github.com/rtfd/readthedocs.org/pull/1849"},"issue":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/issues/1849"},"comments":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/issues/1849/comments"},"review_comments":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1849/comments"},"review_comment":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1849/commits"},"statuses":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/statuses/aaffe6effd166596ce738c1403bd9745262f9ea5"}}}}