myl 0.8.7__py3-none-any.whl → 0.9.3__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,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: myl
3
- Version: 0.8.7
3
+ Version: 0.9.3
4
4
  Summary: Dead simple IMAP CLI client
5
5
  Author-email: Philipp Schmitt <philipp@schmitt.co>
6
- License: GNU GENERAL PUBLIC LICENSE
6
+ License: GNU GENERAL PUBLIC LICENSE
7
7
  Version 3, 29 June 2007
8
8
 
9
9
  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
@@ -678,14 +678,19 @@ License: GNU GENERAL PUBLIC LICENSE
678
678
  Public License instead of this License. But first, please read
679
679
  <https://www.gnu.org/licenses/why-not-lgpl.html>.
680
680
 
681
+ Project-URL: homepage, https://github.com/pschmitt/myl
682
+ Project-URL: documentation, https://github.com/pschmitt/myl/blob/head/readme.md
683
+ Project-URL: repository, https://github.com/pschmitt/myl
684
+ Project-URL: issues, https://github.com/pschmitt/myl/issues
681
685
  Keywords: imap,email
682
686
  Classifier: Programming Language :: Python :: 3
683
687
  Requires-Python: >=3.8
684
688
  Description-Content-Type: text/markdown
685
689
  License-File: LICENSE
686
- Requires-Dist: imap-tools ==1.5.0
687
- Requires-Dist: myl-discovery ==0.5.7
688
- Requires-Dist: rich >=13.7.0
690
+ Requires-Dist: imap-tools<2.0.0,>=1.5.0
691
+ Requires-Dist: myl-discovery>=0.6.1.dev0
692
+ Requires-Dist: rich<14.0.0,>=13.0.0
693
+ Requires-Dist: html2text>=2024.2.26
689
694
 
690
695
  # 📧 myl
691
696
 
@@ -716,15 +721,23 @@ straightforward way to interact with IMAP servers.
716
721
 
717
722
  To install myl, follow these steps:
718
723
 
719
- ```bash
724
+ ```shell
720
725
  pipx install myl
726
+ # or:
727
+ pip install --user myl
728
+ ```
729
+
730
+ on nix you can do this:
731
+
732
+ ```shell
733
+ nix run github:pschmitt/myl -- --help
721
734
  ```
722
735
 
723
736
  ## 🛠️ Usage
724
737
 
725
738
  Here's how you can use myl:
726
739
 
727
- ```bash
740
+ ```shell
728
741
  myl --help
729
742
  ```
730
743
 
@@ -732,7 +745,7 @@ This command will display the help information for the `myl` command.
732
745
 
733
746
  Here are some examples of using flags with the `myl` command:
734
747
 
735
- ```bash
748
+ ```shell
736
749
  # Connect to an IMAP server
737
750
  myl --server imap.example.com --port 143 --starttls --username "$username" --password "$password"
738
751
 
@@ -0,0 +1,7 @@
1
+ myl.py,sha256=8CCrgsD1DhmbZycNJk24XLf_hgESRsf6ku1d6DyUOfA,13954
2
+ myl-0.9.3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
3
+ myl-0.9.3.dist-info/METADATA,sha256=jy8hkerCv6mVFN2Qwv4JoLFE7ZOMoLcKL_zs_cRzTag,43447
4
+ myl-0.9.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
5
+ myl-0.9.3.dist-info/entry_points.txt,sha256=q6nr0Kzim7JzreXQE3BTU4asLh2sx5-D0w1yLBOcHxc,33
6
+ myl-0.9.3.dist-info/top_level.txt,sha256=Wn88OJVVWyYSsKVoqzlHXxfFxh5IbrJ_Yw-ldNLe7Po,4
7
+ myl-0.9.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
myl.py CHANGED
@@ -1,14 +1,33 @@
1
+ #!/usr/bin/env python3
2
+ # coding: utf-8
3
+
4
+ from importlib.metadata import version, PackageNotFoundError
1
5
  import argparse
2
6
  import logging
7
+ import ssl
3
8
  import sys
9
+ from json import dumps as json_dumps
4
10
 
5
- import imap_tools
11
+ import html2text
12
+ from imap_tools.consts import MailMessageFlags
13
+ from imap_tools.mailbox import (
14
+ BaseMailBox,
15
+ MailBox,
16
+ MailBoxTls,
17
+ MailBoxUnencrypted,
18
+ )
19
+ from imap_tools.query import AND
6
20
  from myldiscovery import autodiscover
7
- from rich import print
21
+ from rich import print, print_json
8
22
  from rich.console import Console
9
23
  from rich.logging import RichHandler
10
24
  from rich.table import Table
11
25
 
26
+ try:
27
+ __version__ = version("myl")
28
+ except PackageNotFoundError:
29
+ pass
30
+
12
31
  LOGGER = logging.getLogger(__name__)
13
32
  IMAP_PORT = 993
14
33
  GMAIL_IMAP_SERVER = "imap.gmail.com"
@@ -17,13 +36,94 @@ GMAIL_SENT_FOLDER = "[Gmail]/Sent Mail"
17
36
  # GMAIL_ALL_FOLDER = "[Gmail]/All Mail"
18
37
 
19
38
 
39
+ class MissingServerException(Exception):
40
+ pass
41
+
42
+
20
43
  def error_msg(msg):
21
44
  print(f"[red]{msg}[/red]", file=sys.stderr)
22
45
 
23
46
 
47
+ def mail_to_dict(msg, date_format="%Y-%m-%d %H:%M:%S"):
48
+ return {
49
+ "uid": msg.uid,
50
+ "subject": msg.subject,
51
+ "from": msg.from_,
52
+ "to": msg.to,
53
+ "date": msg.date.strftime(date_format),
54
+ "timestamp": str(int(msg.date.timestamp())),
55
+ "unread": mail_is_unread(msg),
56
+ "flags": msg.flags,
57
+ "content": {
58
+ "raw": msg.obj.as_string(),
59
+ "html": msg.html,
60
+ "text": msg.text,
61
+ },
62
+ "attachments": msg.attachments,
63
+ }
64
+
65
+
66
+ def mail_to_json(msg, date_format="%Y-%m-%d %H:%M:%S"):
67
+ return json_dumps(mail_to_dict(msg, date_format))
68
+
69
+
70
+ def mail_is_unread(msg):
71
+ return MailMessageFlags.SEEN not in msg.flags
72
+
73
+
24
74
  def parse_args():
25
75
  parser = argparse.ArgumentParser()
26
- parser.add_argument("-d", "--debug", help="Debug", action="store_true")
76
+ subparsers = parser.add_subparsers(
77
+ dest="command", help="Available commands"
78
+ )
79
+ parser.add_argument(
80
+ "-V",
81
+ "--version",
82
+ action="version",
83
+ version=f"%(prog)s {__version__}",
84
+ )
85
+
86
+ # Default command: list all emails
87
+ subparsers.add_parser("list", help="List all emails")
88
+
89
+ # Get/show email command
90
+ get_parser = subparsers.add_parser(
91
+ "get", help="Retrieve a specific email or attachment"
92
+ )
93
+ get_parser.add_argument("MAILID", help="Mail ID to fetch", type=int)
94
+ get_parser.add_argument(
95
+ "ATTACHMENT",
96
+ help="Name of the attachment to fetch",
97
+ nargs="?",
98
+ default=None,
99
+ )
100
+
101
+ # Delete email command
102
+ delete_parser = subparsers.add_parser("delete", help="Delete an email")
103
+ delete_parser.add_argument(
104
+ "MAILIDS", help="Mail ID(s) to delete", type=int, nargs="+"
105
+ )
106
+
107
+ # Mark email as read/unread
108
+ mark_read_parser = subparsers.add_parser(
109
+ "read", help="mark an email as read"
110
+ )
111
+ mark_read_parser.add_argument(
112
+ "MAILIDS", help="Mail ID(s) to mark as read", type=int, nargs="+"
113
+ )
114
+ mark_unread_parser = subparsers.add_parser(
115
+ "unread", help="mark an email as unread"
116
+ )
117
+ mark_unread_parser.add_argument(
118
+ "MAILIDS", help="Mail ID(s) to mark as unread", type=int, nargs="+"
119
+ )
120
+
121
+ # Optional arguments
122
+ parser.add_argument(
123
+ "-d", "--debug", help="Enable debug mode", action="store_true"
124
+ )
125
+
126
+ # IMAP connection settings
27
127
  parser.add_argument(
28
128
  "-s", "--server", help="IMAP server address", required=False
29
129
  )
@@ -44,9 +144,30 @@ def parse_args():
44
144
  parser.add_argument(
45
145
  "-P", "--port", help="IMAP server port", default=IMAP_PORT
46
146
  )
147
+ parser.add_argument("--ssl", help="SSL", action="store_true", default=True)
148
+ parser.add_argument(
149
+ "--starttls", help="STARTTLS", action="store_true", default=False
150
+ )
151
+ parser.add_argument(
152
+ "--insecure",
153
+ help="Disable cert validation",
154
+ action="store_true",
155
+ default=False,
156
+ )
157
+
158
+ # Credentials
47
159
  parser.add_argument(
48
- "--starttls", help="Start TLS", action="store_true", default=False
160
+ "-u", "--username", help="IMAP username", required=True
161
+ )
162
+ password_group = parser.add_mutually_exclusive_group(required=True)
163
+ password_group.add_argument("-p", "--password", help="IMAP password")
164
+ password_group.add_argument(
165
+ "--password-file",
166
+ help="IMAP password (file path)",
167
+ type=argparse.FileType("r"),
49
168
  )
169
+
170
+ # Display preferences
50
171
  parser.add_argument(
51
172
  "-c",
52
173
  "--count",
@@ -55,17 +176,22 @@ def parse_args():
55
176
  type=int,
56
177
  )
57
178
  parser.add_argument(
58
- "-m", "--mark-seen", help="Mark seen", action="store_true"
59
- )
60
- parser.add_argument(
61
- "-u", "--username", help="IMAP username", required=True
179
+ "-t", "--no-title", help="Do not show title", action="store_true"
62
180
  )
63
181
  parser.add_argument(
64
- "-p", "--password", help="IMAP password", required=True
182
+ "--date-format", help="Date format", default="%H:%M %d/%m/%Y"
65
183
  )
184
+
185
+ # IMAP actions
66
186
  parser.add_argument(
67
- "-t", "--no-title", help="Do not show title", action="store_true"
187
+ "-m",
188
+ "--mark-seen",
189
+ help="Mark seen",
190
+ action="store_true",
191
+ default=False,
68
192
  )
193
+
194
+ # Email filtering
69
195
  parser.add_argument("-f", "--folder", help="IMAP folder", default="INBOX")
70
196
  parser.add_argument(
71
197
  "--sent",
@@ -73,8 +199,28 @@ def parse_args():
73
199
  action="store_true",
74
200
  )
75
201
  parser.add_argument("-S", "--search", help="Search string", default="ALL")
76
- parser.add_argument("-w", "--wrap", help="Wrap text", action="store_true")
77
- parser.add_argument("-H", "--html", help="Show HTML", action="store_true")
202
+ parser.add_argument(
203
+ "--unread",
204
+ help="Limit to unread emails",
205
+ action="store_true",
206
+ default=False,
207
+ )
208
+
209
+ # Output preferences
210
+ parser.add_argument(
211
+ "-H",
212
+ "--html",
213
+ help="Show HTML email",
214
+ action="store_true",
215
+ default=False,
216
+ )
217
+ parser.add_argument(
218
+ "-j",
219
+ "--json",
220
+ help="JSON output",
221
+ action="store_true",
222
+ default=False,
223
+ )
78
224
  parser.add_argument(
79
225
  "-r",
80
226
  "--raw",
@@ -82,23 +228,14 @@ def parse_args():
82
228
  action="store_true",
83
229
  default=False,
84
230
  )
85
- parser.add_argument("MAILID", help="Mail ID to fetch", nargs="?")
86
- parser.add_argument(
87
- "ATTACHMENT", help="Name of the attachment to fetch", nargs="?"
88
- )
89
231
 
90
232
  return parser.parse_args()
91
233
 
92
234
 
93
- def main():
94
- console = Console()
95
- args = parse_args()
96
- logging.basicConfig(
97
- format="%(message)s",
98
- handlers=[RichHandler(console=console)],
99
- level=logging.DEBUG if args.debug else logging.INFO,
235
+ def mb_connect(console, args) -> BaseMailBox:
236
+ imap_password = args.password or (
237
+ args.password_file and args.password_file.read()
100
238
  )
101
- LOGGER.debug(args)
102
239
 
103
240
  if args.google:
104
241
  args.server = GMAIL_IMAP_SERVER
@@ -113,17 +250,21 @@ def main():
113
250
  if args.auto:
114
251
  try:
115
252
  settings = autodiscover(
116
- args.username, password=args.password
117
- ).get("imap")
253
+ args.username,
254
+ password=imap_password,
255
+ insecure=args.insecure,
256
+ ).get("imap", {})
118
257
  except Exception:
119
258
  error_msg("Failed to autodiscover IMAP settings")
120
259
  if args.debug:
121
260
  console.print_exception(show_locals=True)
122
- return 1
261
+ raise
262
+
123
263
  LOGGER.debug(f"Discovered settings: {settings})")
124
264
  args.server = settings.get("server")
125
265
  args.port = settings.get("port", IMAP_PORT)
126
266
  args.starttls = settings.get("starttls")
267
+ args.ssl = settings.get("ssl")
127
268
 
128
269
  if args.sent:
129
270
  args.folder = "Sent"
@@ -136,76 +277,228 @@ def main():
136
277
  "- set --google if you are using a Gmail account\n"
137
278
  "- use --auto to attempt autodiscovery"
138
279
  )
139
- return 2
280
+ raise MissingServerException()
281
+
282
+ ssl_context = ssl.create_default_context()
283
+ if args.insecure:
284
+ ssl_context.check_hostname = False
285
+ ssl_context.verify_mode = ssl.CERT_NONE
286
+
287
+ mb_kwargs = {"host": args.server, "port": args.port}
288
+ if args.ssl:
289
+ mb = MailBox
290
+ mb_kwargs["ssl_context"] = ssl_context
291
+ elif args.starttls:
292
+ mb = MailBoxTls
293
+ mb_kwargs["ssl_context"] = ssl_context
294
+ else:
295
+ mb = MailBoxUnencrypted
140
296
 
297
+ mailbox = mb(**mb_kwargs)
298
+ mailbox.login(args.username, imap_password, args.folder)
299
+ return mailbox
300
+
301
+
302
+ def display_single_mail(
303
+ mailbox: BaseMailBox,
304
+ mail_id: int,
305
+ attachment: str | None = None,
306
+ mark_seen: bool = False,
307
+ raw: bool = False,
308
+ html: bool = False,
309
+ json: bool = False,
310
+ ):
311
+ LOGGER.debug("Fetch mail %s", mail_id)
312
+ msg = next(mailbox.fetch(f"UID {mail_id}", mark_seen=mark_seen))
313
+ LOGGER.debug("Fetched mail %s", msg)
314
+
315
+ if attachment:
316
+ for att in msg.attachments:
317
+ if att.filename == attachment:
318
+ sys.stdout.buffer.write(att.payload)
319
+ return 0
320
+ print(
321
+ f"attachment {attachment} not found",
322
+ file=sys.stderr,
323
+ )
324
+ return 1
325
+
326
+ if html:
327
+ output = msg.text
328
+ if raw:
329
+ output = msg.html
330
+ else:
331
+ output = html2text.html2text(msg.html)
332
+ print(output)
333
+ elif raw:
334
+ print(msg.obj.as_string())
335
+ return 0
336
+ elif json:
337
+ print_json(mail_to_json(msg))
338
+ return 0
339
+ else:
340
+ print(msg.text)
341
+
342
+ for att in msg.attachments:
343
+ print(f"📎 Attachment: {att.filename}", file=sys.stderr)
344
+ return 0
345
+
346
+
347
+ def display_emails(
348
+ mailbox,
349
+ console,
350
+ no_title=False,
351
+ search="ALL",
352
+ unread_only=False,
353
+ count=10,
354
+ mark_seen=False,
355
+ json=False,
356
+ date_format="%H:%M %d/%m/%Y",
357
+ ):
358
+ json_data = []
141
359
  table = Table(
142
- expand=True,
143
- show_header=not args.no_title,
360
+ show_header=not no_title,
144
361
  header_style="bold",
362
+ expand=True,
145
363
  show_lines=False,
364
+ show_edge=False,
365
+ pad_edge=False,
146
366
  box=None,
367
+ row_styles=["", "dim"],
147
368
  )
148
- table.add_column("ID", style="red", no_wrap=not args.wrap, max_width=10)
149
- table.add_column(
150
- "Subject", style="green", no_wrap=not args.wrap, max_width=30
369
+ table.add_column("ID", style="red", no_wrap=True)
370
+ table.add_column("Subject", style="green", no_wrap=True, ratio=3)
371
+ table.add_column("From", style="blue", no_wrap=True, ratio=2)
372
+ table.add_column("Date", style="cyan", no_wrap=True)
373
+
374
+ if unread_only:
375
+ search = AND(seen=False)
376
+
377
+ for msg in mailbox.fetch(
378
+ criteria=search,
379
+ reverse=True,
380
+ bulk=True,
381
+ limit=count,
382
+ mark_seen=mark_seen,
383
+ headers_only=False, # required for attachments
384
+ ):
385
+ subj_prefix = "🆕 " if mail_is_unread(msg) else ""
386
+ subj_prefix += "📎 " if len(msg.attachments) > 0 else ""
387
+ subject = (
388
+ msg.subject.replace("\n", "") if msg.subject else "<no-subject>"
389
+ )
390
+ if json:
391
+ json_data.append(mail_to_dict(msg))
392
+ else:
393
+ table.add_row(
394
+ msg.uid if msg.uid else "???",
395
+ f"{subj_prefix}{subject}",
396
+ msg.from_,
397
+ (msg.date.strftime(date_format) if msg.date else "???"),
398
+ )
399
+ if table.row_count >= count:
400
+ break
401
+
402
+ if json:
403
+ print_json(json_dumps(json_data))
404
+ else:
405
+ console.print(table)
406
+ if table.row_count == 0:
407
+ print(
408
+ "[yellow italic]No messages[/yellow italic]",
409
+ file=sys.stderr,
410
+ )
411
+ return 0
412
+
413
+
414
+ def delete_emails(mailbox: BaseMailBox, mail_ids: list):
415
+ LOGGER.warning("Deleting mails %s", mail_ids)
416
+ mailbox.delete([str(x) for x in mail_ids])
417
+ return 0
418
+
419
+
420
+ def set_seen(mailbox: BaseMailBox, mail_ids: list, value=True):
421
+ LOGGER.info(
422
+ "Marking mails as %s: %s", "read" if value else "unread", mail_ids
151
423
  )
152
- table.add_column("From", style="blue", no_wrap=not args.wrap, max_width=30)
153
- table.add_column("Date", style="cyan", no_wrap=not args.wrap)
424
+ mailbox.flag(
425
+ [str(x) for x in mail_ids],
426
+ flag_set=(MailMessageFlags.SEEN),
427
+ value=value,
428
+ )
429
+ return 0
430
+
154
431
 
155
- mb = imap_tools.MailBoxTls if args.starttls else imap_tools.MailBox
432
+ def mark_read(mailbox: BaseMailBox, mail_ids: list):
433
+ return set_seen(mailbox, mail_ids, value=True)
434
+
435
+
436
+ def mark_unread(mailbox: BaseMailBox, mail_ids: list):
437
+ return set_seen(mailbox, mail_ids, value=False)
438
+
439
+
440
+ def main() -> int:
441
+ console = Console()
442
+ args = parse_args()
443
+ logging.basicConfig(
444
+ format="%(message)s",
445
+ handlers=[RichHandler(console=console)],
446
+ level=logging.DEBUG if args.debug else logging.INFO,
447
+ )
448
+ LOGGER.debug(args)
156
449
 
157
450
  try:
158
- with mb(args.server, port=args.port).login(
159
- args.username, args.password, args.folder
160
- ) as mailbox:
161
- if args.MAILID:
162
- msg = next(
163
- mailbox.fetch(
164
- f"UID {args.MAILID}", mark_seen=args.mark_seen
165
- )
451
+ with mb_connect(console, args) as mailbox:
452
+ # inbox display
453
+ if args.command in ["list", None]:
454
+ return display_emails(
455
+ mailbox=mailbox,
456
+ console=console,
457
+ no_title=args.no_title,
458
+ search=args.search,
459
+ unread_only=args.unread,
460
+ count=args.count,
461
+ mark_seen=args.mark_seen,
462
+ json=args.json,
463
+ date_format=args.date_format,
166
464
  )
167
- if args.ATTACHMENT:
168
- for att in msg.attachments:
169
- if att.filename == args.ATTACHMENT:
170
- sys.stdout.buffer.write(att.payload)
171
- return 0
172
- print(
173
- f"Attachment {args.ATTACHMENT} not found",
174
- file=sys.stderr,
175
- )
176
- return 1
177
- else:
178
- if args.raw:
179
- print(msg.obj.as_string())
180
- return 0
181
- print(msg.text if not args.html else msg.html)
182
- for att in msg.attachments:
183
- print(
184
- f"📎 Attachment: {att.filename}", file=sys.stderr
185
- )
186
- return 0
187
465
 
188
- for msg in mailbox.fetch(
189
- criteria=args.search,
190
- reverse=True,
191
- bulk=True,
192
- limit=args.count,
193
- mark_seen=args.mark_seen,
194
- headers_only=False, # required for attachments
195
- ):
196
- subj_prefix = "📎 " if len(msg.attachments) > 0 else ""
197
- table.add_row(
198
- msg.uid if msg.uid else "???",
199
- subj_prefix
200
- + (msg.subject if msg.subject else "<no-subject>"),
201
- msg.from_,
202
- msg.date.strftime("%H:%M %d/%m/%Y") if msg.date else "???",
466
+ # single email
467
+ # FIXME $ myl 219 raises an argparse error
468
+ elif args.command in ["get", "show", "display"]:
469
+ return display_single_mail(
470
+ mailbox=mailbox,
471
+ mail_id=args.MAILID,
472
+ attachment=args.ATTACHMENT,
473
+ mark_seen=args.mark_seen,
474
+ raw=args.raw,
475
+ html=args.html,
476
+ json=args.json,
203
477
  )
204
- if len(table.rows) >= args.count:
205
- break
206
478
 
207
- console.print(table)
208
- return 0
479
+ # mark emails as read
480
+ elif args.command in ["read"]:
481
+ return mark_read(
482
+ mailbox=mailbox,
483
+ mail_ids=args.MAILIDS,
484
+ )
485
+
486
+ elif args.command in ["unread"]:
487
+ return mark_unread(
488
+ mailbox=mailbox,
489
+ mail_ids=args.MAILIDS,
490
+ )
491
+
492
+ # delete email
493
+ elif args.command in ["delete", "remove"]:
494
+ return delete_emails(
495
+ mailbox=mailbox,
496
+ mail_ids=args.MAILIDS,
497
+ )
498
+ else:
499
+ error_msg(f"Unknown command: {args.command}")
500
+ return 1
501
+
209
502
  except Exception:
210
503
  console.print_exception(show_locals=True)
211
504
  return 1
@@ -1,7 +0,0 @@
1
- myl.py,sha256=tyXYwSNAkaq2rNasB1OtBldpDE209l6IzvaF_Qa1aZQ,6843
2
- myl-0.8.7.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
3
- myl-0.8.7.dist-info/METADATA,sha256=yz8_wODPddW5YKNmsJT7M6wT4af5kHwLeUzBVxsCICw,43013
4
- myl-0.8.7.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
5
- myl-0.8.7.dist-info/entry_points.txt,sha256=q6nr0Kzim7JzreXQE3BTU4asLh2sx5-D0w1yLBOcHxc,33
6
- myl-0.8.7.dist-info/top_level.txt,sha256=Wn88OJVVWyYSsKVoqzlHXxfFxh5IbrJ_Yw-ldNLe7Po,4
7
- myl-0.8.7.dist-info/RECORD,,
File without changes