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.
- pyship/__init__.py +2 -1
- pyship/__main__.py +0 -1
- pyship/_version_.py +1 -1
- pyship/app_info.py +12 -27
- pyship/clip.py +18 -104
- pyship/cloud.py +2 -1
- pyship/constants.py +7 -0
- pyship/create_launcher.py +48 -81
- pyship/download.py +18 -2
- pyship/launcher/__init__.py +1 -1
- pyship/launcher/launcher_standalone.py +309 -0
- pyship/launcher/metadata.py +5 -0
- pyship/launcher_stub.py +198 -0
- pyship/nsis.py +12 -5
- pyship/pyship.py +11 -9
- pyship/pyship_custom_print.py +0 -1
- pyship/pyship_get_icon.py +3 -1
- pyship/uv_util.py +157 -0
- pyship-0.3.1.dist-info/METADATA +81 -0
- pyship-0.3.1.dist-info/RECORD +34 -0
- {pyship-0.1.6.dist-info → pyship-0.3.1.dist-info}/WHEEL +1 -1
- pyship/atomic_zip.py +0 -41
- pyship/get-pip.py +0 -22713
- pyship/launcher/launcher.py +0 -224
- pyship/patch/__init__.py +0 -0
- pyship/patch/pyship_patch.py +0 -33
- pyship-0.1.6.dist-info/METADATA +0 -47
- pyship-0.1.6.dist-info/RECORD +0 -36
- {pyship-0.1.6.dist-info → pyship-0.3.1.dist-info/licenses}/LICENSE +0 -0
- {pyship-0.1.6.dist-info → pyship-0.3.1.dist-info}/top_level.txt +0 -0
pyship/launcher/launcher.py
DELETED
|
@@ -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
|
pyship/patch/pyship_patch.py
DELETED
|
@@ -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()
|
pyship-0.1.6.dist-info/METADATA
DELETED
|
@@ -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)
|
pyship-0.1.6.dist-info/RECORD
DELETED
|
@@ -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,,
|
|
File without changes
|
|
File without changes
|