pyship 0.1.5__py3-none-any.whl → 0.1.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyship/_version_.py +1 -1
- pyship/app_info.py +28 -3
- pyship/arguments.py +0 -1
- pyship/create_launcher.py +17 -7
- pyship/download.py +18 -2
- pyship/launcher/launcher.py +6 -9
- pyship/main.py +0 -1
- pyship/nsis.py +0 -1
- pyship/pyship.py +3 -4
- pyship/pyship_subprocess.py +0 -1
- {pyship-0.1.5.dist-info → pyship-0.1.7.dist-info}/METADATA +47 -50
- {pyship-0.1.5.dist-info → pyship-0.1.7.dist-info}/RECORD +15 -15
- {pyship-0.1.5.dist-info → pyship-0.1.7.dist-info}/WHEEL +1 -1
- {pyship-0.1.5.dist-info → pyship-0.1.7.dist-info}/LICENSE +0 -0
- {pyship-0.1.5.dist-info → pyship-0.1.7.dist-info}/top_level.txt +0 -0
pyship/_version_.py
CHANGED
|
@@ -2,7 +2,7 @@ from pyshipupdate import __author__ as pyship_author
|
|
|
2
2
|
|
|
3
3
|
__application_name__ = "pyship"
|
|
4
4
|
__author__ = pyship_author
|
|
5
|
-
__version__ = "0.1.
|
|
5
|
+
__version__ = "0.1.7"
|
|
6
6
|
__title__ = __application_name__
|
|
7
7
|
__author_email__ = "j@abel.co"
|
|
8
8
|
__url__ = "https://github.com/jamesabel/pyship"
|
pyship/app_info.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
from typing import Union
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
4
7
|
|
|
5
8
|
import toml
|
|
6
9
|
from semver import VersionInfo
|
|
@@ -49,13 +52,11 @@ def get_app_info_py_project(app_info: AppInfo, target_app_project_dir: Path) ->
|
|
|
49
52
|
pyproject = toml.load(f)
|
|
50
53
|
project_section = pyproject.get("project")
|
|
51
54
|
if project_section is not None:
|
|
52
|
-
|
|
53
55
|
app_info.name = project_section.get("name")
|
|
54
56
|
app_info.author = project_section.get("author") # app author
|
|
55
57
|
|
|
56
58
|
tool_section = pyproject.get("tool")
|
|
57
59
|
if tool_section is not None:
|
|
58
|
-
|
|
59
60
|
if app_info.name is None:
|
|
60
61
|
# The user didn't provide a separate [project].name so let's try to get it from what flit writes out at [tool.flit.metadata]/module.
|
|
61
62
|
# This is all we want or need to get from tool.flit.metadata since the remainder of the fields will be in the package distribution.
|
|
@@ -86,6 +87,16 @@ def get_app_info_wheel(app_info: AppInfo, dist_path: Path) -> AppInfo:
|
|
|
86
87
|
return app_info
|
|
87
88
|
|
|
88
89
|
|
|
90
|
+
@typechecked
|
|
91
|
+
def run_script(target_app_project_dir: Path, script_file_name: str):
|
|
92
|
+
script_path = Path(target_app_project_dir, script_file_name)
|
|
93
|
+
if script_path.exists():
|
|
94
|
+
pyship_print(f'running "{script_file_name}" (cwd="{target_app_project_dir}")')
|
|
95
|
+
make_venv_process = subprocess.run(script_file_name, cwd=str(target_app_project_dir), capture_output=True)
|
|
96
|
+
log.info(make_venv_process.stdout)
|
|
97
|
+
log.info(make_venv_process.stderr)
|
|
98
|
+
|
|
99
|
+
|
|
89
100
|
@typechecked
|
|
90
101
|
def get_app_info(target_app_project_dir: Path, target_app_dist_dir: Path) -> AppInfo:
|
|
91
102
|
"""
|
|
@@ -103,8 +114,22 @@ def get_app_info(target_app_project_dir: Path, target_app_dist_dir: Path) -> App
|
|
|
103
114
|
if app_info.name is None:
|
|
104
115
|
log.error(f"{app_info.name=} {target_app_project_dir=}")
|
|
105
116
|
else:
|
|
117
|
+
wheel_glob = f"{app_info.name}*.whl"
|
|
118
|
+
wheel_list = list(target_app_dist_dir.glob(wheel_glob))
|
|
119
|
+
if len(wheel_list) < 1:
|
|
120
|
+
# No wheel file exists, but if there's a .bat file to build it, try that. In general the pyship user should have already created their wheel, but this is also
|
|
121
|
+
# handy for testing so the pyship repo can be cloned and pytest run, without otherwise having to build the test app's wheel.
|
|
122
|
+
|
|
123
|
+
venvs = list(Path(target_app_project_dir).glob("*venv")) # does venv or .venv exist?
|
|
124
|
+
if len(venvs) < 1:
|
|
125
|
+
# Try to build the venv if it doesn't exist. Note that this uses specific script file names, but they can be set via env var.
|
|
126
|
+
run_script(target_app_project_dir, os.environ.get("PYSHIP_MAKE_VENV_SCRIPT", "make_venv.bat")) # will be .sh for Linux/MacOS whenever they're supported ...
|
|
127
|
+
|
|
128
|
+
# run script to build the wheel
|
|
129
|
+
run_script(target_app_project_dir, os.environ.get("PYSHIP_BUILD_SCRIPT", "build.bat")) # will be .sh for Linux/MacOS whenever they're supported ...
|
|
130
|
+
|
|
131
|
+
wheel_list = list(target_app_dist_dir.glob(wheel_glob)) # try again to find the wheel
|
|
106
132
|
|
|
107
|
-
wheel_list = list(target_app_dist_dir.glob(f"{app_info.name}*.whl"))
|
|
108
133
|
if len(wheel_list) == 0:
|
|
109
134
|
log.error(f"{app_info.name} : no wheel at {target_app_dist_dir} ({target_app_dist_dir.absolute()})")
|
|
110
135
|
elif len(wheel_list) > 1:
|
pyship/arguments.py
CHANGED
|
@@ -8,7 +8,6 @@ from pyship import __name__, __version__, DEFAULT_DIST_DIR_NAME
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def get_arguments() -> Any:
|
|
11
|
-
|
|
12
11
|
parser = argparse.ArgumentParser(prog=__name__, formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
13
12
|
|
|
14
13
|
parser.add_argument("-n", "--name", help="name of target application to ship (if not provided elsewhere such as in pyproject.toml at: [project] name=<name>")
|
pyship/create_launcher.py
CHANGED
|
@@ -4,6 +4,7 @@ import subprocess
|
|
|
4
4
|
|
|
5
5
|
from typeguard import typechecked
|
|
6
6
|
from balsa import get_logger
|
|
7
|
+
from semver import VersionInfo
|
|
7
8
|
|
|
8
9
|
from pyshipupdate import mkdirs
|
|
9
10
|
import pyship
|
|
@@ -30,7 +31,6 @@ def create_pyship_launcher(target_app_info: AppInfo, app_path_output: Path):
|
|
|
30
31
|
if target_app_info.name is None or len(target_app_info.name) < 1:
|
|
31
32
|
log.error(f"{target_app_info.name=}")
|
|
32
33
|
else:
|
|
33
|
-
|
|
34
34
|
metadata_filename = f"{target_app_info.name}_metadata.json"
|
|
35
35
|
|
|
36
36
|
# create launcher
|
|
@@ -52,7 +52,6 @@ def create_pyship_launcher(target_app_info: AppInfo, app_path_output: Path):
|
|
|
52
52
|
if python_interpreter_path is None or len(python_interpreter_path) < 1:
|
|
53
53
|
log.error("python interpreter path not found")
|
|
54
54
|
else:
|
|
55
|
-
|
|
56
55
|
mkdirs(app_path_output)
|
|
57
56
|
|
|
58
57
|
explicit_modules_to_import = [
|
|
@@ -82,17 +81,28 @@ def create_pyship_launcher(target_app_info: AppInfo, app_path_output: Path):
|
|
|
82
81
|
command_line.append("--noconsole")
|
|
83
82
|
# command_line.extend(["--debug", "all"]) # todo: remove once we get the launcher working again
|
|
84
83
|
site_packages_dir = Path(venv_dir, "Lib", "site-packages")
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
|
|
85
|
+
# find the launcher source code, so we can make an executable from it
|
|
86
|
+
launcher_source_path = None
|
|
87
|
+
launcher_candidate_parents = (site_packages_dir, Path())
|
|
88
|
+
for parent in launcher_candidate_parents:
|
|
89
|
+
source_candidate = Path(parent, pyship_application_name, launcher_application_name, f"{launcher_application_name}.py").absolute()
|
|
90
|
+
if source_candidate.exists():
|
|
91
|
+
launcher_source_path = source_candidate
|
|
92
|
+
break
|
|
93
|
+
if launcher_source_path is None:
|
|
94
|
+
log.fatal(f"could not find launcher path in {launcher_candidate_parents=}")
|
|
95
|
+
return False
|
|
96
|
+
else:
|
|
97
|
+
log.info(f"{launcher_source_path=}")
|
|
98
|
+
command_line.append(str(launcher_source_path))
|
|
89
99
|
|
|
90
100
|
# avoid re-building launcher if its functionality wouldn't change
|
|
91
101
|
assert isinstance(target_app_info.author, str)
|
|
92
102
|
assert isinstance(target_app_info.is_gui, bool)
|
|
103
|
+
assert isinstance(target_app_info.version, VersionInfo)
|
|
93
104
|
metadata = calculate_metadata(target_app_info.name, target_app_info.author, target_app_info.version, Path(launcher_module_dir), icon_path, target_app_info.is_gui)
|
|
94
105
|
if not launcher_exe_path.exists() or metadata != load_metadata(app_path_output, metadata_filename):
|
|
95
|
-
|
|
96
106
|
pyship_print(f'building launcher ("{launcher_exe_path}")')
|
|
97
107
|
log.info(f"project_dir={str(target_app_info.project_dir)}")
|
|
98
108
|
log.info(f"{command_line=}")
|
pyship/download.py
CHANGED
|
@@ -32,6 +32,22 @@ def file_download(url: str, destination_folder: Path, file_name: Path):
|
|
|
32
32
|
return destination_path
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
def is_within_directory(directory: Path, target: Path):
|
|
36
|
+
abs_directory = directory.absolute()
|
|
37
|
+
abs_target = target.absolute()
|
|
38
|
+
prefix = os.path.commonprefix([abs_directory, abs_target])
|
|
39
|
+
return prefix == str(abs_directory)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def safe_extract(tar, path: Path = Path(".")):
|
|
43
|
+
# for CVE-2007-4559
|
|
44
|
+
for member in tar.getmembers():
|
|
45
|
+
member_path = Path(path, member.name)
|
|
46
|
+
if not is_within_directory(path, member_path):
|
|
47
|
+
raise Exception("Attempted Path Traversal in Tar File")
|
|
48
|
+
tar.extractall(path, None, numeric_owner=False)
|
|
49
|
+
|
|
50
|
+
|
|
35
51
|
@typechecked
|
|
36
52
|
def extract(source_folder: Path, source_file: Path, destination_folder: Path):
|
|
37
53
|
mkdirs(destination_folder, remove_first=True)
|
|
@@ -43,9 +59,9 @@ def extract(source_folder: Path, source_file: Path, destination_folder: Path):
|
|
|
43
59
|
zf.extractall(destination_folder)
|
|
44
60
|
elif source_file.suffix == ".tgz":
|
|
45
61
|
with tarfile.open(source) as tf:
|
|
46
|
-
tf
|
|
62
|
+
safe_extract(tf, destination_folder)
|
|
47
63
|
elif source_file.suffix == ".gz":
|
|
48
64
|
with tarfile.open(source) as tf:
|
|
49
|
-
tf
|
|
65
|
+
safe_extract(tf, destination_folder)
|
|
50
66
|
else:
|
|
51
67
|
raise Exception(f"Unsupported file type {source_file.suffix} ({source_file})")
|
pyship/launcher/launcher.py
CHANGED
|
@@ -6,6 +6,7 @@ import appdirs
|
|
|
6
6
|
import re
|
|
7
7
|
import logging
|
|
8
8
|
import subprocess
|
|
9
|
+
from typing import Union
|
|
9
10
|
|
|
10
11
|
from ismain import is_main
|
|
11
12
|
from balsa import HandlerType, get_logger
|
|
@@ -27,7 +28,6 @@ launcher_verbose_string = "--launcher_verbose"
|
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def setup_logging(is_gui: bool, report_exceptions: bool) -> bool:
|
|
30
|
-
|
|
31
31
|
verbose = len(sys.argv) > 1 and sys.argv[1].lower() == launcher_verbose_string
|
|
32
32
|
|
|
33
33
|
pyship_log = PyshipLog(launcher_application_name, __author__, gui=is_gui, verbose=verbose)
|
|
@@ -74,7 +74,7 @@ def setup_logging(is_gui: bool, report_exceptions: bool) -> bool:
|
|
|
74
74
|
return verbose
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
def launch(additional_path: Path = None, app_dir: Path = None) -> int:
|
|
77
|
+
def launch(additional_path: Union[Path, None] = None, app_dir: Union[Path, None] = None) -> int:
|
|
78
78
|
"""
|
|
79
79
|
launch the pyship app
|
|
80
80
|
:param additional_path - additional search path for app (mainly for testing)
|
|
@@ -98,8 +98,8 @@ def launch(additional_path: Path = None, app_dir: Path = None) -> int:
|
|
|
98
98
|
target_app_author = __author__
|
|
99
99
|
|
|
100
100
|
for metadata_file_path in app_dir.glob("*_metadata.json"):
|
|
101
|
-
with metadata_file_path.open() as
|
|
102
|
-
metadata = json.load(
|
|
101
|
+
with metadata_file_path.open() as metadata_file:
|
|
102
|
+
metadata = json.load(metadata_file)
|
|
103
103
|
target_app_name = metadata.get("app")
|
|
104
104
|
target_app_author = metadata.get("author", target_app_author)
|
|
105
105
|
is_gui = metadata.get("is_gui", is_gui)
|
|
@@ -112,7 +112,6 @@ def launch(additional_path: Path = None, app_dir: Path = None) -> int:
|
|
|
112
112
|
if target_app_name is None:
|
|
113
113
|
log.error(f'could not derive target app name in {app_dir}")')
|
|
114
114
|
else:
|
|
115
|
-
|
|
116
115
|
log.info(f"{target_app_name=}")
|
|
117
116
|
|
|
118
117
|
# 1) find the latest <application_name>_<version>
|
|
@@ -125,7 +124,6 @@ def launch(additional_path: Path = None, app_dir: Path = None) -> int:
|
|
|
125
124
|
restart_monitor = RestartMonitor()
|
|
126
125
|
|
|
127
126
|
while (return_code is None or return_code == restart_return_code) and not restart_monitor.excessive():
|
|
128
|
-
|
|
129
127
|
restart_monitor.add()
|
|
130
128
|
|
|
131
129
|
# todo: put finding the most recent app version in a function - I'll pretty sure this is done other places. Also, it allows a unit test to be written for it.
|
|
@@ -171,7 +169,6 @@ def launch(additional_path: Path = None, app_dir: Path = None) -> int:
|
|
|
171
169
|
cmd.append(arg) # pass along any arguments to the target application
|
|
172
170
|
log.info(f"{cmd}")
|
|
173
171
|
try:
|
|
174
|
-
|
|
175
172
|
# todo: this should work with PyInstaller, but it doesn't and I don't know why:
|
|
176
173
|
# return_code, _, _ = subprocess_run(cmd, cwd=python_exe_path.parent, mute_output=is_gui) # if app returns "restart_value" then it wants to be restarted
|
|
177
174
|
|
|
@@ -191,7 +188,7 @@ def launch(additional_path: Path = None, app_dir: Path = None) -> int:
|
|
|
191
188
|
log_function(out)
|
|
192
189
|
|
|
193
190
|
# log, and possibly print, each line of output from the process
|
|
194
|
-
for name, std_x,
|
|
191
|
+
for name, std_x, sys_f in [("stdout", std_out, sys.stdout), ("stderr", std_err, sys.stderr)]:
|
|
195
192
|
if std_x is not None and len(std_x.strip()) > 0:
|
|
196
193
|
for so_line in std_x.splitlines():
|
|
197
194
|
so_line_strip = so_line.strip()
|
|
@@ -199,7 +196,7 @@ def launch(additional_path: Path = None, app_dir: Path = None) -> int:
|
|
|
199
196
|
log.info(f"{name}:{so_line_strip}") # when logging, start with the name of the output string (stdout, stderr)
|
|
200
197
|
|
|
201
198
|
# output stdout, stderr that came (directly) from the process
|
|
202
|
-
print(std_x, file=
|
|
199
|
+
print(std_x, file=sys_f)
|
|
203
200
|
|
|
204
201
|
log.info(f"{return_code=}")
|
|
205
202
|
|
pyship/main.py
CHANGED
pyship/nsis.py
CHANGED
|
@@ -48,7 +48,6 @@ def run_nsis(target_app_info: AppInfo, target_app_version: VersionInfo, app_dir:
|
|
|
48
48
|
license_file_name = "LICENSE"
|
|
49
49
|
|
|
50
50
|
if Path(target_app_info.project_dir, license_file_name).exists():
|
|
51
|
-
|
|
52
51
|
nsis_file_path = Path(target_app_info.project_dir, f"{target_app_info.name}.nsi").absolute()
|
|
53
52
|
pyship_print(f'building installer "{nsis_file_path}" ("{nsis_file_path.absolute()}")')
|
|
54
53
|
|
pyship/pyship.py
CHANGED
|
@@ -7,6 +7,7 @@ from attr import attrs
|
|
|
7
7
|
from typeguard import typechecked
|
|
8
8
|
from awsimple import S3Access
|
|
9
9
|
from balsa import get_logger
|
|
10
|
+
from semver import VersionInfo
|
|
10
11
|
|
|
11
12
|
from pyship import __application_name__ as pyship_application_name
|
|
12
13
|
from pyship import __author__ as pyship_author
|
|
@@ -21,7 +22,6 @@ log = get_logger(pyship_application_name)
|
|
|
21
22
|
|
|
22
23
|
@attrs(auto_attribs=True)
|
|
23
24
|
class PyShip:
|
|
24
|
-
|
|
25
25
|
project_dir: Path = Path() # target app project dir, e.g. the "home" directory of the project. If not set, current working directory is used.
|
|
26
26
|
dist_dir: Path = Path(DEFAULT_DIST_DIR_NAME) # many packaging tools (e.g filt, etc.) use "dist" as the package destination directory
|
|
27
27
|
find_links: list = list() # extra dirs for pip to use for packages not yet on PyPI (e.g. under local development)
|
|
@@ -33,7 +33,7 @@ class PyShip:
|
|
|
33
33
|
cloud_id: Union[str, None] = None # e.g. AWS Access Key ID
|
|
34
34
|
cloud_secret: Union[str, None] = None # e.g. AWS Secret Access Key
|
|
35
35
|
cloud_access: Union[PyShipCloud, None] = None # making this accessible outside this class aids in testing, especially when mocking
|
|
36
|
-
upload: bool = True # set to False to tell pyship to not attempt to perform file upload to the cloud (e.g. installer, clip files to AWS S3)
|
|
36
|
+
upload: bool = True # set to False in order to tell pyship to not attempt to perform file upload to the cloud (e.g. installer, clip files to AWS S3)
|
|
37
37
|
|
|
38
38
|
@typechecked
|
|
39
39
|
def ship(self) -> Path:
|
|
@@ -55,7 +55,6 @@ class PyShip:
|
|
|
55
55
|
elif target_app_info.name is None:
|
|
56
56
|
raise PyshipNoAppName
|
|
57
57
|
else:
|
|
58
|
-
|
|
59
58
|
app_dir = Path(self.project_dir, APP_DIR_NAME, target_app_info.name).absolute()
|
|
60
59
|
|
|
61
60
|
mkdirs(app_dir, remove_first=True)
|
|
@@ -65,13 +64,13 @@ class PyShip:
|
|
|
65
64
|
clip_dir = create_clip(target_app_info, app_dir, True, Path(self.project_dir, self.dist_dir), self.cache_dir, self.find_links)
|
|
66
65
|
|
|
67
66
|
clip_file_path = create_clip_file(clip_dir) # create clip file
|
|
67
|
+
assert isinstance(target_app_info.version, VersionInfo)
|
|
68
68
|
installer_exe_path = run_nsis(target_app_info, target_app_info.version, app_dir) # create installer
|
|
69
69
|
|
|
70
70
|
if self.upload:
|
|
71
71
|
if self.cloud_profile is None and self.cloud_id is None:
|
|
72
72
|
pyship_print("no cloud access provided - will not attempt upload")
|
|
73
73
|
else:
|
|
74
|
-
|
|
75
74
|
# if cloud bucket not given we'll try to use the project name
|
|
76
75
|
assert isinstance(target_app_info.name, str)
|
|
77
76
|
assert isinstance(target_app_info.author, str)
|
pyship/pyship_subprocess.py
CHANGED
|
@@ -1,50 +1,47 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: pyship
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: freezer, installer and updater for Python applications
|
|
5
|
-
Home-page: https://github.com/jamesabel/pyship
|
|
6
|
-
|
|
7
|
-
Author
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Keywords: freezer,installer,ship
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Requires-Dist:
|
|
15
|
-
Requires-Dist:
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist:
|
|
18
|
-
Requires-Dist:
|
|
19
|
-
Requires-Dist:
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
[Short video on pyship given at Pyninsula](https://abelpublic.s3.us-west-2.amazonaws.com/pyship_pyninsula_10_2020.mkv)
|
|
49
|
-
|
|
50
|
-
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pyship
|
|
3
|
+
Version: 0.1.7
|
|
4
|
+
Summary: freezer, installer and updater for Python applications
|
|
5
|
+
Home-page: https://github.com/jamesabel/pyship
|
|
6
|
+
Download-URL: https://github.com/jamesabel/pyship
|
|
7
|
+
Author: abel
|
|
8
|
+
Author-email: j@abel.co
|
|
9
|
+
License: MIT License
|
|
10
|
+
Keywords: freezer,installer,ship
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: setuptools
|
|
14
|
+
Requires-Dist: wheel
|
|
15
|
+
Requires-Dist: ismain
|
|
16
|
+
Requires-Dist: balsa
|
|
17
|
+
Requires-Dist: requests
|
|
18
|
+
Requires-Dist: attrs
|
|
19
|
+
Requires-Dist: typeguard
|
|
20
|
+
Requires-Dist: toml
|
|
21
|
+
Requires-Dist: pyinstaller
|
|
22
|
+
Requires-Dist: semver
|
|
23
|
+
Requires-Dist: python-dateutil
|
|
24
|
+
Requires-Dist: wheel-inspect
|
|
25
|
+
Requires-Dist: boto3
|
|
26
|
+
Requires-Dist: awsimple
|
|
27
|
+
Requires-Dist: pyshipupdate
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# PyShip
|
|
31
|
+
|
|
32
|
+
Enables shipping a python application to end users.
|
|
33
|
+
|
|
34
|
+
## PyShip's Major Features
|
|
35
|
+
|
|
36
|
+
* Freeze practically any Python application
|
|
37
|
+
* Creates an installer
|
|
38
|
+
* Uploads application installer and updates to the cloud
|
|
39
|
+
* Automatic application updating in the background (no user intervention)
|
|
40
|
+
* OS native application (e.g. .exe for Windows)
|
|
41
|
+
* Run on OS startup option
|
|
42
|
+
|
|
43
|
+
## Documentation and Examples
|
|
44
|
+
|
|
45
|
+
[Learn PyShip By Example](https://github.com/jamesabel/pyshipexample)
|
|
46
|
+
|
|
47
|
+
[Short video on pyship given at Pyninsula](https://abelpublic.s3.us-west-2.amazonaws.com/pyship_pyninsula_10_2020.mkv)
|
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
pyship/__init__.py,sha256=x7jisL_D4Wuyw5tfmyLz57WlRLX9QQYpkb3Vne_J3W0,1336
|
|
2
2
|
pyship/__main__.py,sha256=mn9BUEsE52gyI7SLrXh_dBxLwD335iVG_iqRCNqocq4,86
|
|
3
|
-
pyship/_version_.py,sha256=
|
|
4
|
-
pyship/app_info.py,sha256=
|
|
5
|
-
pyship/arguments.py,sha256=
|
|
3
|
+
pyship/_version_.py,sha256=_lpeahvzNw7RLUbyDSBVoziS-tN4LJxasGcrBlkALBo,389
|
|
4
|
+
pyship/app_info.py,sha256=mhbwJHkoWek1FRv9dwRlrf_8ssNK-UtDhvnZ-8kWQFo,7347
|
|
5
|
+
pyship/arguments.py,sha256=p4cA8FwbNSZqg4XI6LsYit1QN7TpAWWZH2xrgLf-f-g,1882
|
|
6
6
|
pyship/atomic_zip.py,sha256=HEZuHEatyWAGqZcvS_EoByQQRCQph5ajkszfZ3OxnRU,1366
|
|
7
7
|
pyship/clip.py,sha256=kmdbnzJbgD9ggBEUqs73_p2kac2looaOeGItk9N50xo,8237
|
|
8
8
|
pyship/cloud.py,sha256=FDJvTlURrtA00z3a8fWkR_K7YzURNKnd0NypJQKxHAo,1264
|
|
9
9
|
pyship/constants.py,sha256=FVkr1c9XDkaXyQnL1bEP-dWSbk4SUkrRuVQYPIxHMaA,19
|
|
10
|
-
pyship/create_launcher.py,sha256=
|
|
11
|
-
pyship/download.py,sha256=
|
|
10
|
+
pyship/create_launcher.py,sha256=HQby4dmnBnSbohspex8TaCgu-jEaEjLb1QGE76D_ky4,6247
|
|
11
|
+
pyship/download.py,sha256=QHbdVwxLNBToMEC0tOF2ZquDpoydRu0-cj7LOERr5Q4,2460
|
|
12
12
|
pyship/get-pip.py,sha256=BC5otuX7znr6F8Llv4oQLwLDp22R0GwHmThf0UIHRHI,1829954
|
|
13
13
|
pyship/logging.py,sha256=cIB7487tN8TsKeDHwwzjA30rHRW5Ci5GA5uPbaEXaY0,966
|
|
14
|
-
pyship/main.py,sha256=
|
|
15
|
-
pyship/nsis.py,sha256=
|
|
14
|
+
pyship/main.py,sha256=pALpy8ooWsKkUpWUtGWO567I9u1fbIFWdBU0dnWm2K4,866
|
|
15
|
+
pyship/nsis.py,sha256=QhG8FMwFZRsoPQbntCSCr5av1NNqgjksinhfxXpc0Vg,12744
|
|
16
16
|
pyship/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
pyship/pyship.ico,sha256=9oiMtmA7NiBmxM0NmMJQU5jXmnbEWCdqTaO3XvJ7ozY,103500
|
|
18
|
-
pyship/pyship.py,sha256=
|
|
18
|
+
pyship/pyship.py,sha256=vMeFK940mIReKTMy0x-hFvTHXfATt97tL5knyK7R_9I,5554
|
|
19
19
|
pyship/pyship_custom_print.py,sha256=WHgZNZNiKwQjKmxdsBrn7aJfG5VJdfQ1-9AC_tg5MxE,651
|
|
20
20
|
pyship/pyship_exceptions.py,sha256=teUOh3FuRUU9hyv7L4owACQZPWNdwpYTEz6l-Ao35nw,959
|
|
21
21
|
pyship/pyship_get_icon.py,sha256=FwglA67RXX0lhW9baIGsQy3_T4hVsVkBmvAhPyuhHLo,2016
|
|
22
22
|
pyship/pyship_path.py,sha256=RpU2n7pWXTr0501S7U8KxWCpBc8jOqKPuaX0X6IWPI8,262
|
|
23
|
-
pyship/pyship_subprocess.py,sha256=
|
|
23
|
+
pyship/pyship_subprocess.py,sha256=hH4MW_j2zLJLDmLDkkl2JWupXrvSemAeCPAEtPdSGyk,3212
|
|
24
24
|
pyship/launcher/__init__.py,sha256=25vVghziYIHDBA0mCaGqhj6hWxxjQqlm4LrY0Hem0YY,245
|
|
25
25
|
pyship/launcher/__main__.py,sha256=KhFSf1RU5Cj15y0c4FlzvIvDpWWi9NKYy0gzJ-w7KQE,115
|
|
26
26
|
pyship/launcher/hash.py,sha256=THEh8G-StEJeh5tH24RnAQpYB0Zv1nnBwqOoStN6_NY,1153
|
|
27
|
-
pyship/launcher/launcher.py,sha256=
|
|
27
|
+
pyship/launcher/launcher.py,sha256=AmjAwt8VbQTOVmRC-vUd7Cm-KS3ALYe1RLSxmcutthY,10359
|
|
28
28
|
pyship/launcher/metadata.py,sha256=vkf70w_eX8EZbMDHFKqBWW1kn0RyZl70z4DGsLCahmA,1666
|
|
29
29
|
pyship/launcher/restart_monitor.py,sha256=jfzchLxGuC_xUNdPAr849FhAdozJ6BX9M-gqLd-gGIA,952
|
|
30
30
|
pyship/patch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
31
|
pyship/patch/pyship_patch.py,sha256=KgObD80QPeOhu1KCdhdQzn-4AnyzCUxIGWKj22BjTCc,1256
|
|
32
|
-
pyship-0.1.
|
|
33
|
-
pyship-0.1.
|
|
34
|
-
pyship-0.1.
|
|
35
|
-
pyship-0.1.
|
|
36
|
-
pyship-0.1.
|
|
32
|
+
pyship-0.1.7.dist-info/LICENSE,sha256=wJpPh6rC_YhbTZ7ZIy1VjrqeLdJ8Pz0ppx2MUi7s8Dg,1088
|
|
33
|
+
pyship-0.1.7.dist-info/METADATA,sha256=0vrDjPX1xcZbErIhF_WDmKFPVRgviw4a0iJhEPLUm00,1361
|
|
34
|
+
pyship-0.1.7.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
35
|
+
pyship-0.1.7.dist-info/top_level.txt,sha256=mlozeft-UInVAceSajzNvtkn2vPa8El9j0RulsTVtlU,7
|
|
36
|
+
pyship-0.1.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|