pyship 0.1.6__py3-none-any.whl → 0.3.1__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.
@@ -1,224 +0,0 @@
1
- import sys
2
- from pathlib import Path
3
- from semver import VersionInfo
4
- import json
5
- import appdirs
6
- import re
7
- import logging
8
- import subprocess
9
-
10
- from ismain import is_main
11
- from balsa import HandlerType, get_logger
12
- import requests
13
- import sentry_sdk
14
-
15
- from pyship import __application_name__, __author__
16
- from pyship import __version__ as pyship_version
17
- from pyship import PyshipLog, python_interpreter_exes
18
- from pyshipupdate import restart_return_code, error_return_code, can_not_find_file_return_code, ok_return_code
19
- from pyship.launcher import RestartMonitor
20
-
21
- # Just for the launcher, not the user's app that pyship is launching
22
- launcher_application_name = f"{__application_name__}_launcher"
23
-
24
- log = get_logger(launcher_application_name)
25
-
26
- launcher_verbose_string = "--launcher_verbose"
27
-
28
-
29
- def setup_logging(is_gui: bool, report_exceptions: bool) -> bool:
30
- verbose = len(sys.argv) > 1 and sys.argv[1].lower() == launcher_verbose_string
31
-
32
- pyship_log = PyshipLog(launcher_application_name, __author__, gui=is_gui, verbose=verbose)
33
-
34
- if verbose:
35
- print(f"{pyship_log.log_path=}")
36
-
37
- exception_string = "" # store exception strings here until logging gets set up
38
-
39
- if report_exceptions:
40
- # use Sentry's exception service
41
- sentry_dsn = None
42
- try:
43
- response = requests.get("https://api.pyship.org/resources/pyship/sentry")
44
- if response.status_code == 200:
45
- try:
46
- sentry_dsn = json.loads(response.text)["dsn"]
47
- except json.decoder.JSONDecodeError as e:
48
- exception_string = str(e)
49
- except KeyError as e:
50
- exception_string = str(e)
51
- except requests.exceptions.RequestException as e:
52
- exception_string = str(e)
53
- if sentry_dsn is not None:
54
- pyship_log.sentry_dsn = sentry_dsn
55
-
56
- # init sentry outside of balsa and turn off integrations to workaround bug:
57
- # ModuleNotFoundError: No module named 'sentry_sdk.integrations.excepthook'
58
- pyship_log.use_sentry = False
59
- sentry_sdk.init(sentry_dsn, default_integrations=False)
60
-
61
- pyship_log.init_logger()
62
- # UI log at a high level since the user will not see launcher output (unless something goes terribly wrong)
63
- for ht in [HandlerType.DialogBox, HandlerType.Console]:
64
- if ht in pyship_log.handlers:
65
- pyship_log.handlers[ht].setLevel(logging.ERROR)
66
-
67
- if len(exception_string) > 0:
68
- log.info(exception_string) # don't present these to the user unless verbose selected
69
-
70
- log.info(f"{verbose=}")
71
- log.info(f"{pyship_version=}")
72
-
73
- return verbose
74
-
75
-
76
- def launch(additional_path: Path = None, app_dir: Path = None) -> int:
77
- """
78
- launch the pyship app
79
- :param additional_path - additional search path for app (mainly for testing)
80
- :param app_dir - override app dir (mainly for testing)
81
- :return: 0 if no error, non-zero on error (like Windows apps)
82
- """
83
-
84
- return_code = None
85
-
86
- # derive the target app name based on the clip dir(s) that exist
87
- clip_regex_string = "([_a-z0-9]*)_([.0-9]+)"
88
- clip_regex = re.compile(clip_regex_string, flags=re.IGNORECASE) # simple format that accepts common semver (but not all semver)
89
-
90
- if app_dir is None:
91
- app_dir = Path(sys.executable).parent.parent.resolve().absolute() # up one dir from where the python interpreter is
92
-
93
- # these should be set below, but in case there's no metadata file set them to something to allow the logging to be set up
94
- is_gui = False
95
- report_exceptions = True
96
- target_app_name = __application_name__
97
- target_app_author = __author__
98
-
99
- for metadata_file_path in app_dir.glob("*_metadata.json"):
100
- with metadata_file_path.open() as f:
101
- metadata = json.load(f)
102
- target_app_name = metadata.get("app")
103
- target_app_author = metadata.get("author", target_app_author)
104
- is_gui = metadata.get("is_gui", is_gui)
105
- report_exceptions = metadata.get("report_exceptions", report_exceptions)
106
-
107
- setup_logging(is_gui, report_exceptions)
108
-
109
- log.info(f"{app_dir=}")
110
-
111
- if target_app_name is None:
112
- log.error(f'could not derive target app name in {app_dir}")')
113
- else:
114
- log.info(f"{target_app_name=}")
115
-
116
- # 1) find the latest <application_name>_<version>
117
- # 2) execute it via python -m <application_name>
118
- # 3) inside the program, it has option to call pyship module's upgrade() function. If an upgrade happens, upgrade() will return True
119
- # and the program has the option to request a restart by returning the restart_return_code exit code.
120
-
121
- glob_string = f"{target_app_name}_*" # clip directories will be of this form
122
-
123
- restart_monitor = RestartMonitor()
124
-
125
- while (return_code is None or return_code == restart_return_code) and not restart_monitor.excessive():
126
- restart_monitor.add()
127
-
128
- # 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.
129
- # find the most recent app version
130
-
131
- search_dirs = [app_dir, Path(appdirs.user_data_dir(target_app_name, target_app_author))]
132
- if additional_path is not None:
133
- search_dirs.append(additional_path)
134
-
135
- candidate_dirs = []
136
- for search_dir in search_dirs:
137
- for d in search_dir.glob(glob_string):
138
- if d.is_dir():
139
- candidate_dirs.append(d)
140
-
141
- versions = {}
142
- for candidate_dir in candidate_dirs:
143
- matches = re.match(clip_regex, candidate_dir.name)
144
- if matches is not None:
145
- version = matches.group(2)
146
- try:
147
- semver = VersionInfo.parse(version)
148
- except ValueError:
149
- semver = None
150
- if semver is not None:
151
- versions[semver] = candidate_dir
152
- else:
153
- log.error(f"could not get version out of {candidate_dir}")
154
-
155
- if len(versions) > 0:
156
- latest_version = sorted(versions.keys())[-1]
157
- log.info(f"{latest_version=}")
158
-
159
- # locate the python interpreter executable
160
- python_exe_path = Path(versions[latest_version], python_interpreter_exes[is_gui])
161
-
162
- # run the target app using the python interpreter we just found
163
- if python_exe_path.exists():
164
- cmd = [str(python_exe_path), "-m", target_app_name]
165
- if len(sys.argv) > 1:
166
- for arg in sys.argv[1:]:
167
- if arg != launcher_verbose_string:
168
- cmd.append(arg) # pass along any arguments to the target application
169
- log.info(f"{cmd}")
170
- try:
171
- # todo: this should work with PyInstaller, but it doesn't and I don't know why:
172
- # 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
173
-
174
- # todo: so do this instead:
175
- target_process = subprocess.run(cmd, cwd=python_exe_path.parent, capture_output=True, text=True)
176
- return_code = target_process.returncode # if app returns "restart_value" then it wants to be restarted
177
-
178
- # Treatment of the output from the subprocess run in launcher is similar - but not exactly - the same as is in pyship/pyship_subprocess.py, so keep them separate for now.
179
- # Maybe eventually we'll have just one shared routine once enough tests are in place.
180
- std_out = target_process.stdout
181
- std_err = target_process.stderr
182
-
183
- if (std_err is not None and len(std_err.strip()) > 0) or (target_process.returncode != ok_return_code and target_process.returncode != restart_return_code):
184
- # if there's a problem, log it with what the caller provides
185
- for out, log_function in [(std_out, log.warning), (std_err, log.error)]:
186
- if out is not None and len(out.strip()) > 0:
187
- log_function(out)
188
-
189
- # log, and possibly print, each line of output from the process
190
- for name, std_x, f in [("stdout", std_out, sys.stdout), ("stderr", std_err, sys.stderr)]:
191
- if std_x is not None and len(std_x.strip()) > 0:
192
- for so_line in std_x.splitlines():
193
- so_line_strip = so_line.strip()
194
- if len(so_line_strip) > 0:
195
- log.info(f"{name}:{so_line_strip}") # when logging, start with the name of the output string (stdout, stderr)
196
-
197
- # output stdout, stderr that came (directly) from the process
198
- print(std_x, file=f)
199
-
200
- log.info(f"{return_code=}")
201
-
202
- except FileNotFoundError as e:
203
- log.error(f"{e} {cmd}")
204
- return_code = error_return_code
205
- else:
206
- log.error(f"python exe not found at {python_exe_path}")
207
- return_code = can_not_find_file_return_code
208
-
209
- else:
210
- log.error(f"could not find any expected application version in {search_dirs} ({glob_string=}))")
211
-
212
- if restart_monitor.excessive():
213
- log.error(f"excessive restarts {restart_monitor.restarts=}")
214
-
215
- if return_code is None:
216
- return_code = error_return_code
217
-
218
- log.info(f"returning : {return_code=}")
219
-
220
- return return_code
221
-
222
-
223
- if is_main():
224
- sys.exit(launch())
pyship/patch/__init__.py DELETED
File without changes
@@ -1,33 +0,0 @@
1
- def pyship_patch():
2
- # this is a function in order to load its source (via inspect.getsource() ), that is written to pyship_patch.py (without the function definition)
3
-
4
- # I got this error:
5
- # "AttributeError: 'zipimporter' object has no attribute 'exec_module'"
6
- #
7
- # and found this workaround which is implemented below
8
- # https://stackoverflow.com/questions/63574951/cant-start-python-script-in-android-studio-via-chaquopy-after-including-datepar
9
- #
10
- # along with this hack to implement it in the Python environment:
11
- # https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html
12
- #
13
- # this must accompany pyship_patch.pth (i.e. in the same directory) to load this upon Python interpreter startup. In other words, they both must be put in the clip directory
14
- # (where python.exe resides).
15
- #
16
- # pyship_patch.pth contents:
17
- #
18
- # import pyship_patch
19
- #
20
- from zipimport import zipimporter
21
-
22
- def create_module(self, spec):
23
- return None
24
-
25
- zipimporter.create_module = create_module
26
-
27
- def exec_module(self, module):
28
- exec(self.get_code(module.__name__), module.__dict__)
29
-
30
- zipimporter.exec_module = exec_module
31
-
32
-
33
- pyship_patch()
@@ -1,47 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: pyship
3
- Version: 0.1.6
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 +0,0 @@
1
- pyship/__init__.py,sha256=x7jisL_D4Wuyw5tfmyLz57WlRLX9QQYpkb3Vne_J3W0,1336
2
- pyship/__main__.py,sha256=mn9BUEsE52gyI7SLrXh_dBxLwD335iVG_iqRCNqocq4,86
3
- pyship/_version_.py,sha256=s-tyBvcMBAL-DIOAUGlD0JGMe-peqnUrSIg7hxunfSY,389
4
- pyship/app_info.py,sha256=mhbwJHkoWek1FRv9dwRlrf_8ssNK-UtDhvnZ-8kWQFo,7347
5
- pyship/arguments.py,sha256=p4cA8FwbNSZqg4XI6LsYit1QN7TpAWWZH2xrgLf-f-g,1882
6
- pyship/atomic_zip.py,sha256=HEZuHEatyWAGqZcvS_EoByQQRCQph5ajkszfZ3OxnRU,1366
7
- pyship/clip.py,sha256=kmdbnzJbgD9ggBEUqs73_p2kac2looaOeGItk9N50xo,8237
8
- pyship/cloud.py,sha256=FDJvTlURrtA00z3a8fWkR_K7YzURNKnd0NypJQKxHAo,1264
9
- pyship/constants.py,sha256=FVkr1c9XDkaXyQnL1bEP-dWSbk4SUkrRuVQYPIxHMaA,19
10
- pyship/create_launcher.py,sha256=vIZi5ABVmfXKFc9IHjYMkyAVbRKkSIxWgvHSdIyNrD8,6146
11
- pyship/download.py,sha256=inG18XKZ1qhOam27prKT0cTUx1IXRQooUPiIezZ_xf8,1871
12
- pyship/get-pip.py,sha256=BC5otuX7znr6F8Llv4oQLwLDp22R0GwHmThf0UIHRHI,1829954
13
- pyship/logging.py,sha256=cIB7487tN8TsKeDHwwzjA30rHRW5Ci5GA5uPbaEXaY0,966
14
- pyship/main.py,sha256=pALpy8ooWsKkUpWUtGWO567I9u1fbIFWdBU0dnWm2K4,866
15
- pyship/nsis.py,sha256=QhG8FMwFZRsoPQbntCSCr5av1NNqgjksinhfxXpc0Vg,12744
16
- pyship/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- pyship/pyship.ico,sha256=9oiMtmA7NiBmxM0NmMJQU5jXmnbEWCdqTaO3XvJ7ozY,103500
18
- pyship/pyship.py,sha256=tqTd6kOU7i_LP9VCfSCcGZBpGU8OfPiCxb7arRAIGuc,5444
19
- pyship/pyship_custom_print.py,sha256=WHgZNZNiKwQjKmxdsBrn7aJfG5VJdfQ1-9AC_tg5MxE,651
20
- pyship/pyship_exceptions.py,sha256=teUOh3FuRUU9hyv7L4owACQZPWNdwpYTEz6l-Ao35nw,959
21
- pyship/pyship_get_icon.py,sha256=FwglA67RXX0lhW9baIGsQy3_T4hVsVkBmvAhPyuhHLo,2016
22
- pyship/pyship_path.py,sha256=RpU2n7pWXTr0501S7U8KxWCpBc8jOqKPuaX0X6IWPI8,262
23
- pyship/pyship_subprocess.py,sha256=hH4MW_j2zLJLDmLDkkl2JWupXrvSemAeCPAEtPdSGyk,3212
24
- pyship/launcher/__init__.py,sha256=25vVghziYIHDBA0mCaGqhj6hWxxjQqlm4LrY0Hem0YY,245
25
- pyship/launcher/__main__.py,sha256=KhFSf1RU5Cj15y0c4FlzvIvDpWWi9NKYy0gzJ-w7KQE,115
26
- pyship/launcher/hash.py,sha256=THEh8G-StEJeh5tH24RnAQpYB0Zv1nnBwqOoStN6_NY,1153
27
- pyship/launcher/launcher.py,sha256=XI0X4NvYYxz88dYf9_Fk7D1D_Y0qC1ZRfef1RFB4r5g,10275
28
- pyship/launcher/metadata.py,sha256=vkf70w_eX8EZbMDHFKqBWW1kn0RyZl70z4DGsLCahmA,1666
29
- pyship/launcher/restart_monitor.py,sha256=jfzchLxGuC_xUNdPAr849FhAdozJ6BX9M-gqLd-gGIA,952
30
- pyship/patch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- pyship/patch/pyship_patch.py,sha256=KgObD80QPeOhu1KCdhdQzn-4AnyzCUxIGWKj22BjTCc,1256
32
- pyship-0.1.6.dist-info/LICENSE,sha256=wJpPh6rC_YhbTZ7ZIy1VjrqeLdJ8Pz0ppx2MUi7s8Dg,1088
33
- pyship-0.1.6.dist-info/METADATA,sha256=ZEZv2MqEiTzDYtbXX3_6u-xhynfaIFW3lP2wEFYLWw8,1361
34
- pyship-0.1.6.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
35
- pyship-0.1.6.dist-info/top_level.txt,sha256=mlozeft-UInVAceSajzNvtkn2vPa8El9j0RulsTVtlU,7
36
- pyship-0.1.6.dist-info/RECORD,,