Repository: python-poetry/poetry

Issue:
Poetry installation instructions for offline machine
- [x] I have searched the [issues](https://github.com/python-poetry/poetry/issues) of this repo and believe that this is not a duplicate.

## Issue

Can we please provide instructions on how to reasonably install poetry to a machine without Internet access? Such a server would use internal pip repos and of course poetry can be installed via pip but I understand the standalone installation is preferable.



Discussion:
Isn't it the same process as installing any other lib from a private pypi on an intranet and then managing environments / Python paths correctly?
poetry's documentation states that it's preferable to install via the shell script and not via pip. I'm specifically curious about how to perform the shell install if the target machine doesn't have direct access to the internet.
I poked around the `get-poetry.py` script and it looks like the `Installer.make_lib()` downloads the requested Poetry version from https://github.com/python-poetry/poetry/releases/download/{version}/. I think what needs to be done to make the above case happen is to split the downloading into it's own method and then have `make_lib` act on a file specified. If the file is already present, then that file can just be passed to the `make_lib` function instead of having to download it. I'm happy to take a crack at this in the near future assuming I'm understanding your use case @Derynect. I'm envisioning an experience sort-of like:

`python get-poetry.py --file poetry-1.0.0.tar.gz`
I think that would be a great solution!

Resolving patch:
diff --git a/get-poetry.py b/get-poetry.py
--- a/get-poetry.py
+++ b/get-poetry.py
@@ -334,6 +334,7 @@ def __init__(
         preview=False,
         force=False,
         accept_all=False,
+        file=None,
         base_url=BASE_URL,
     ):
         self._version = version
@@ -341,6 +342,7 @@ def __init__(
         self._force = force
         self._modify_path = True
         self._accept_all = accept_all
+        self._offline_file = file
         self._base_url = base_url
 
     def allows_prereleases(self):
@@ -357,7 +359,9 @@ def run(self):
         self.ensure_home()
 
         try:
-            self.install(version, upgrade=current_version is not None)
+            self.install(
+                version, upgrade=current_version is not None, file=self._offline_file
+            )
         except subprocess.CalledProcessError as e:
             print(colorize("error", "An error has occured: {}".format(str(e))))
             print(e.output.decode())
@@ -378,6 +382,34 @@ def uninstall(self):
         self.remove_from_path()
 
     def get_version(self):
+        current_version = None
+        if os.path.exists(POETRY_LIB):
+            with open(
+                os.path.join(POETRY_LIB, "poetry", "__version__.py"), encoding="utf-8"
+            ) as f:
+                version_content = f.read()
+
+            current_version_re = re.match(
+                '(?ms).*__version__ = "(.+)".*', version_content
+            )
+            if not current_version_re:
+                print(
+                    colorize(
+                        "warning",
+                        "Unable to get the current Poetry version. Assuming None",
+                    )
+                )
+            else:
+                current_version = current_version_re.group(1)
+
+        # Skip retrieving online release versions if install file is specified
+        if self._offline_file is not None:
+            if current_version is not None and not self._force:
+                print("There is a version of Poetry already installed.")
+                return None, current_version
+
+            return "from an offline file", current_version
+
         print(colorize("info", "Retrieving Poetry metadata"))
 
         metadata = json.loads(self._get(self.METADATA_URL).decode())
@@ -417,26 +449,6 @@ def _compare_versions(x, y):
 
                 break
 
-        current_version = None
-        if os.path.exists(POETRY_LIB):
-            with open(
-                os.path.join(POETRY_LIB, "poetry", "__version__.py"), encoding="utf-8"
-            ) as f:
-                version_content = f.read()
-
-            current_version_re = re.match(
-                '(?ms).*__version__ = "(.+)".*', version_content
-            )
-            if not current_version_re:
-                print(
-                    colorize(
-                        "warning",
-                        "Unable to get the current Poetry version. Assuming None",
-                    )
-                )
-            else:
-                current_version = current_version_re.group(1)
-
         if current_version == version and not self._force:
             print("Latest version already installed.")
             return None, current_version
@@ -484,11 +496,14 @@ def remove_home(self):
 
         shutil.rmtree(POETRY_HOME)
 
-    def install(self, version, upgrade=False):
+    def install(self, version, upgrade=False, file=None):
         """
         Installs Poetry in $POETRY_HOME.
         """
-        print("Installing version: " + colorize("info", version))
+        if file is not None:
+            print("Attempting to install from file: " + colorize("info", file))
+        else:
+            print("Installing version: " + colorize("info", version))
 
         self.make_lib(version)
         self.make_bin()
@@ -524,6 +539,14 @@ def make_lib(self, version):
                 shutil.rmtree(POETRY_LIB_BACKUP)
 
     def _make_lib(self, version):
+        # Check if an offline installer file has been specified
+        if self._offline_file is not None:
+            try:
+                self.extract_lib(self._offline_file)
+                return
+            except Exception:
+                raise RuntimeError("Could not install from offline file.")
+
         # We get the payload from the remote host
         platform = sys.platform
         if platform == "linux2":
@@ -583,12 +606,15 @@ def _make_lib(self, version):
                     )
                 )
 
-            gz = GzipFile(tar, mode="rb")
-            try:
-                with tarfile.TarFile(tar, fileobj=gz, format=tarfile.PAX_FORMAT) as f:
-                    f.extractall(POETRY_LIB)
-            finally:
-                gz.close()
+            self.extract_lib(tar)
+
+    def extract_lib(self, filename):
+        gz = GzipFile(filename, mode="rb")
+        try:
+            with tarfile.TarFile(filename, fileobj=gz, format=tarfile.PAX_FORMAT) as f:
+                f.extractall(POETRY_LIB)
+        finally:
+            gz.close()
 
     def make_bin(self):
         if not os.path.exists(POETRY_BIN):
@@ -931,17 +957,26 @@ def main():
     parser.add_argument(
         "--uninstall", dest="uninstall", action="store_true", default=False
     )
+    parser.add_argument(
+        "--file",
+        dest="file",
+        action="store",
+        help="Install from a local file instead of fetching the latest version "
+        "of Poetry available online.",
+    )
 
     args = parser.parse_args()
 
     base_url = Installer.BASE_URL
-    try:
-        urlopen(Installer.REPOSITORY_URL)
-    except HTTPError as e:
-        if e.code == 404:
-            base_url = Installer.FALLBACK_BASE_URL
-        else:
-            raise
+
+    if args.file is None:
+        try:
+            urlopen(Installer.REPOSITORY_URL)
+        except HTTPError as e:
+            if e.code == 404:
+                base_url = Installer.FALLBACK_BASE_URL
+            else:
+                raise
 
     installer = Installer(
         version=args.version or os.getenv("POETRY_VERSION"),
@@ -950,6 +985,7 @@ def main():
         accept_all=args.accept_all
         or string_to_bool(os.getenv("POETRY_ACCEPT", "0"))
         or not is_interactive(),
+        file=args.file,
         base_url=base_url,
     )
 


Test patch: