poxy 0.15.0__py3-none-any.whl → 0.17.0__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.
@@ -26,7 +26,7 @@ body > header {
26
26
  top: 0;
27
27
  left: 0;
28
28
  right: 0;
29
- z-index: 5;
29
+ z-index: 6;
30
30
  }
31
31
 
32
32
  body > main {
poxy/generated/poxy.css CHANGED
@@ -2638,7 +2638,7 @@ color: var(--color);
2638
2638
  html { height: 100%; scroll-padding-top: 4em; }
2639
2639
  body { min-height: 100%; }
2640
2640
  body { display: flex; flex-direction: column; }
2641
- body > header { position: sticky; top: 0; left: 0; right: 0; z-index: 5; }
2641
+ body > header { position: sticky; top: 0; left: 0; right: 0; z-index: 6; }
2642
2642
  body > main { flex: 1 0 auto; }
2643
2643
  body > footer { flex-shrink: 0; }
2644
2644
  body > header,
poxy/main.py CHANGED
@@ -10,9 +10,15 @@ The various entry-point methods used when poxy is invoked from the command line.
10
10
  import argparse
11
11
  import datetime
12
12
  import os
13
+ import shutil
13
14
  import subprocess
14
15
  import sys
16
+ import typing
15
17
  import zipfile
18
+ from pathlib import Path
19
+
20
+ import colorama
21
+ from colorama import Fore, Style
16
22
 
17
23
  from . import css, doxygen, emoji, graph, mcss, paths
18
24
  from .run import run
@@ -22,10 +28,11 @@ from .version import *
22
28
 
23
29
 
24
30
  def _invoker(func, **kwargs):
31
+ colorama.init()
25
32
  try:
26
33
  func(**kwargs)
27
34
  except WarningTreatedAsError as err:
28
- print(rf'Error: {err} (warning treated as error)', file=sys.stderr)
35
+ print(rf'{Style.BRIGHT}{Fore.RED}error:{Style.RESET_ALL} {err} (warning treated as error)', file=sys.stderr)
29
36
  sys.exit(1)
30
37
  except graph.GraphError as err:
31
38
  print_exception(err, include_type=True, include_traceback=True, skip_frames=1)
@@ -34,17 +41,17 @@ def _invoker(func, **kwargs):
34
41
  print(err, file=sys.stderr)
35
42
  sys.exit(1)
36
43
  except Error as err:
37
- print(rf'Error: {err}', file=sys.stderr)
44
+ print(rf'{Style.BRIGHT}{Fore.RED}error:{Style.RESET_ALL} {err}', file=sys.stderr)
38
45
  sys.exit(1)
39
46
  except Exception as err:
40
- print('\n*************\n', file=sys.stderr)
47
+ print(f'\n{Fore.RED}*************{Style.RESET_ALL}\n', file=sys.stderr)
41
48
  print_exception(err, include_type=True, include_traceback=True, skip_frames=1)
42
49
  print(
43
- '*************\n'
50
+ f'{Fore.RED}*************\n'
44
51
  '\nYou appear to have triggered an internal bug!'
45
52
  '\nPlease re-run poxy with --bug-report and file an issue at github.com/marzer/poxy/issues'
46
53
  '\nMany thanks!'
47
- '\n\n*************',
54
+ f'\n\n*************{Style.RESET_ALL}',
48
55
  file=sys.stderr,
49
56
  )
50
57
  sys.exit(1)
@@ -66,42 +73,309 @@ def make_boolean_optional_arg(args, name, default, help='', **kwargs):
66
73
  args.set_defaults(**{name: default})
67
74
 
68
75
 
69
- def bug_report():
70
- BUG_REPORT_STRIP_ARGS = (
71
- r'--bug-report',
72
- r'--bug-report-internal',
73
- r'-v',
74
- r'--verbose',
75
- r'--color',
76
- r'--no-color',
77
- r'--colour',
78
- r'--no-colour',
76
+ def git(git_args: str, cwd=None) -> typing.Tuple[int, str, str]:
77
+ assert git_args is not None
78
+ proc = subprocess.run(
79
+ ['git'] + str(git_args).strip().split(),
80
+ capture_output=True,
81
+ cwd=str(Path.cwd() if cwd is None else cwd),
82
+ encoding='utf-8',
79
83
  )
84
+ return (proc.returncode, proc.stdout.strip() if proc.stdout else "", proc.stderr.strip() if proc.stderr else "")
85
+
86
+
87
+ def git_failed(git_output: typing.Tuple[int, str, str], desc=''):
88
+ assert git_output is not None
89
+ message = rf"git command failed"
90
+ if desc:
91
+ message += rf': {desc}'
92
+ if git_output[0] != 0: # return code
93
+ message += f'\n {Style.BRIGHT}exit code:{Style.RESET_ALL}\n {git_output[0]}'
94
+ if git_output[1]: # stdout
95
+ message += f'\n {Style.BRIGHT}stdout:{Style.RESET_ALL}\n'
96
+ message += "\n".join([rf' {i.strip()}' for i in git_output[1].split('\n') if i.strip()])
97
+ if git_output[2]: # stderr
98
+ message += f'\n {Style.BRIGHT}stderr:{Style.RESET_ALL}\n'
99
+ message += "\n".join([rf' {i.strip()}' for i in git_output[2].split('\n') if i.strip()])
100
+ raise Error(message)
101
+
102
+
103
+ def git_failed_if_nonzero(git_output: typing.Tuple[int, str, str], desc=''):
104
+ if git_output[0] != 0:
105
+ git_failed(git_output, desc)
106
+ return git_output
107
+
108
+
109
+ def multi_version_git_tags(args: argparse.Namespace):
110
+ print('Running in git-tags mode')
111
+
112
+ input_dir = args.config.resolve()
113
+ if input_dir.is_file():
114
+ input_dir = input_dir.parent
115
+
116
+ if git('diff-files --quiet', cwd=input_dir)[0] or git('diff --no-ext-diff --cached --quiet', cwd=input_dir)[0]:
117
+ raise Error(rf'repository has uncommitted changes')
118
+
119
+ print('Fetching...')
120
+ git_failed_if_nonzero(git('fetch --tags', cwd=input_dir))
121
+
122
+ current_branch = git_failed_if_nonzero(git('rev-parse --abbrev-ref HEAD', cwd=input_dir))[1]
123
+ print(rf'Current branch: {current_branch}')
124
+ original_branch = current_branch
125
+
126
+ default_branch = git_failed_if_nonzero(git('rev-parse --abbrev-ref origin/HEAD', cwd=input_dir))[1]
127
+ default_branch = default_branch.removeprefix(r'origin/')
128
+ print(rf'Default branch: {default_branch}')
129
+
130
+ tags = git_failed_if_nonzero(git('tag', cwd=input_dir))[1].splitlines()
131
+ tags = [(t, t.strip().upper().lstrip('V').lstrip()) for t in tags]
132
+ tags = [(t, v) for t, v in tags if v]
133
+ tags = [(t, re.sub(r'\s+', '', v)) for t, v in tags]
134
+ tags = [(t, re.fullmatch(r'[0-9]+([.][0-9]+([.][0-9]+([.][0-9]+)?)?)?', v)) for t, v in tags]
135
+ tags = [(t, v) for t, v in tags if v]
136
+ tags = [(t, v[0].split('.')) for t, v in tags]
137
+ tags = [
138
+ (t, (int(v[0]), int(v[1]) if len(v) > 1 else 0, int(v[2]) if len(v) > 2 else 0, int(v[3]) if len(v) > 3 else 0))
139
+ for t, v in tags
140
+ ]
141
+ tags = sorted(tags, key=lambda t: t[1], reverse=True)
142
+ tags.insert(0, (default_branch, (999999, 999999, 999999, 999999)))
143
+
144
+ if args.squash_patches:
145
+ # squash patch/rev differences
146
+ seen_versions = set()
147
+ for i in range(len(tags)):
148
+ normalized_version = (tags[i][1][0], tags[i][1][1])
149
+ if normalized_version in seen_versions:
150
+ tags[i] = None
151
+ continue
152
+ seen_versions.add(normalized_version)
153
+ tags = [t for t in tags if t]
154
+
155
+ if args.min_version is not None:
156
+ args.min_version = re.sub(r'[ \t]+', '', str(args.min_version).strip())
157
+ m = re.fullmatch(r'^[vV]?([0-9]+)(?:[.]([0-9]+)(?:[.]([0-9]+)(?:[.]([0-9]+))?)?)?$', args.min_version)
158
+ if m:
159
+ min_ver = (
160
+ (int(m[1] if m[1] else 0)),
161
+ (int(m[2] if m[2] else 0)),
162
+ (int(m[3] if m[3] else 0)),
163
+ (int(m[4] if m[4] else 0)),
164
+ )
165
+ tags = [t for t in tags if t[1] >= min_ver]
166
+ else:
167
+ try:
168
+ max_vers = int(args.min_version)
169
+ assert max_vers < 0
170
+ tags = tags[:-max_vers]
171
+ except:
172
+ raise Error(rf'min-version: expected semver tag or negative integer')
173
+
174
+ tags = [t for t, _ in tags]
175
+ print("Versions:")
176
+ print("\n".join([rf' {t}' for t in tags]))
177
+
178
+ worker_args = [
179
+ arg
180
+ for arg in sys.argv[1:]
181
+ if arg
182
+ not in (
183
+ r'--bug-report',
184
+ r'--git-tags',
185
+ r'--worker',
186
+ r'--versions-in-navbar',
187
+ r'--verbose',
188
+ r'-v',
189
+ r'--higest-patch-only',
190
+ )
191
+ ]
192
+ for key in (r'--output-dir', r'--temp-dir', r'--copy-config-to'):
193
+ pos = -1
194
+ try:
195
+ pos = worker_args.index(key)
196
+ except:
197
+ pass
198
+ if pos != -1:
199
+ worker_args.pop(pos)
200
+ worker_args.pop(pos)
201
+
202
+ tags_temp_dir = paths.TEMP / 'tags' / temp_dir_name_for(str(Path.cwd()))
203
+ delete_directory(tags_temp_dir)
204
+ tags_temp_dir.mkdir(exist_ok=True, parents=True)
205
+
206
+ def cleanup():
207
+ nonlocal current_branch
208
+ nonlocal original_branch
209
+ nonlocal tags_temp_dir
210
+ nonlocal args
211
+ if not args.nocleanup:
212
+ delete_directory(tags_temp_dir)
213
+ if current_branch != original_branch:
214
+ print(rf'Switching back to {Style.BRIGHT}{original_branch}{Style.RESET_ALL}')
215
+ git_failed_if_nonzero(git(rf'checkout {original_branch}', cwd=input_dir))
216
+ current_branch = original_branch
217
+
218
+ emitted_tags = set()
219
+ with Defer(cleanup):
220
+ for tag in tags:
221
+ if current_branch != tag:
222
+ print(rf'Checking out {Style.BRIGHT}{tag}{Style.RESET_ALL}')
223
+ git_failed_if_nonzero(git(rf'checkout {tag}', cwd=input_dir))
224
+ current_branch = tag
225
+
226
+ if current_branch == default_branch:
227
+ git_failed_if_nonzero(git(rf'pull', cwd=input_dir))
228
+
229
+ output_dir = args.output_dir.resolve()
230
+ if tag != default_branch:
231
+ output_dir = tags_temp_dir / tag
232
+ output_dir.mkdir(exist_ok=True, parents=True)
233
+
234
+ try:
235
+ print(rf'Generating documentation for {Style.BRIGHT}{tag}{Style.RESET_ALL}')
236
+ result = subprocess.run(
237
+ args=[
238
+ r'poxy',
239
+ r'--worker',
240
+ r'--versions-in-navbar',
241
+ r'--output-dir',
242
+ str(output_dir),
243
+ *worker_args,
244
+ ],
245
+ cwd=str(Path.cwd()),
246
+ capture_output=not args.verbose,
247
+ encoding='utf-8',
248
+ )
249
+ if result.returncode != 0:
250
+ raise Error(
251
+ rf'Poxy exited with code {result.returncode}{"" if args.verbose else " (re-run with --verbose to see worker output)"}'
252
+ )
253
+ except Exception as exc:
254
+ msg = rf'documentation generation failed for {Style.BRIGHT}{tag}{Style.RESET_ALL}: {exc}'
255
+ if args.werror:
256
+ raise WarningTreatedAsError(msg)
257
+ else:
258
+ print(rf'{Style.BRIGHT}{Fore.YELLOW}warning:{Style.RESET_ALL} {msg}', file=sys.stderr)
259
+ continue
260
+
261
+ if tag != default_branch:
262
+ source_dir = output_dir
263
+ output_dir = args.output_dir.resolve()
264
+ for src in ('html' if args.html else None, 'xml' if args.xml else None):
265
+ if src is None:
266
+ continue
267
+ src = source_dir / src
268
+ if not src.is_dir():
269
+ continue
270
+ dest = output_dir / src.name / tag
271
+ dest.mkdir(exist_ok=True, parents=True)
272
+ delete_directory(src / 'poxy')
273
+ delete_file(src / 'poxy_changelog.html')
274
+ delete_file(src / 'md_poxy_changelog.html')
275
+ shutil.copytree(str(src), str(dest), dirs_exist_ok=True)
276
+ if not args.nocleanup:
277
+ delete_directory(src)
278
+
279
+ emitted_tags.add(tag)
280
+
281
+ if not args.html:
282
+ return
283
+
284
+ print("Linking versions in HTML output")
285
+ tags = [t for t in tags if t in emitted_tags]
286
+ html_root = args.output_dir.resolve() / 'html'
287
+ for tag in tags:
288
+ html_dir = html_root
289
+ if tag != default_branch:
290
+ html_dir /= tag
291
+ assert_existing_directory(html_dir)
292
+ for fp in get_all_files(html_dir, any=('*.css', '*.html', '*.js'), recursive=False):
293
+ text = read_all_text_from_file(fp)
294
+ if tag != default_branch:
295
+ text = text.replace('href="poxy/', 'href="../poxy/')
296
+ text = text.replace('href="poxy_changelog.html', 'href="../poxy_changelog.html')
297
+ text = text.replace('href="md_poxy_changelog.html', 'href="../md_poxy_changelog.html')
298
+ text = text.replace('src="poxy/', 'src="../poxy/')
299
+ versions = rf'<li class="poxy-navbar-version-selector"><a href="{fp.name}">Version: {"HEAD" if tag == default_branch else tag}</a><ol>'
300
+ for dest_tag in tags:
301
+ target = html_root
302
+ if dest_tag != default_branch:
303
+ target /= dest_tag
304
+ assert target.is_dir()
305
+ target /= fp.name
306
+ if not target.is_file():
307
+ target = target.parent / 'index.html'
308
+ assert target.is_file()
309
+ if tag == default_branch and dest_tag != default_branch:
310
+ target = rf'{dest_tag}/{target.name}'
311
+ elif tag != default_branch and dest_tag == default_branch:
312
+ target = rf'../{target.name}'
313
+ elif tag != dest_tag:
314
+ target = rf'../{dest_tag}/{target.name}'
315
+ else:
316
+ target = target.name
317
+ versions += (
318
+ rf'<li><a href="{str(target)}">{"HEAD" if dest_tag == default_branch else dest_tag}</a></li>'
319
+ )
320
+ versions += rf'</ol></li>'
321
+ text = re.sub(
322
+ r'<li>\s*<span\s+class="poxy-navbar-version-selector"\s*>\s*FIXME\s*</span>\s*</li>',
323
+ versions,
324
+ text,
325
+ flags=re.I,
326
+ )
327
+ with open(fp, r'w', newline='\n', encoding=r'utf-8') as f:
328
+ f.write(text)
329
+
330
+
331
+ def bug_report(args: argparse.Namespace):
332
+ bug_report_args = [arg for arg in sys.argv[1:] if arg not in (r'--bug-report', r'--worker', r'-v', r'--verbose')]
333
+ for key in (r'--output-dir', r'--temp-dir', r'--copy-config-to'):
334
+ pos = -1
335
+ try:
336
+ pos = bug_report_args.index(key)
337
+ except:
338
+ pass
339
+ if pos != -1:
340
+ bug_report_args.pop(pos)
341
+ bug_report_args.pop(pos)
342
+
343
+ if '--git-tags' in bug_report_args:
344
+ raise Error(r'--git-tags is currently incompatible with --bug-report. This will be fixed in a later version!')
80
345
 
81
- bug_report_args = [arg for arg in sys.argv[1:] if arg not in BUG_REPORT_STRIP_ARGS]
82
346
  bug_report_zip = (Path.cwd() / r'poxy_bug_report.zip').resolve()
83
347
 
84
348
  print(r'Preparing output paths')
85
349
  delete_directory(paths.BUG_REPORT_DIR)
86
350
  delete_file(bug_report_zip)
87
- os.makedirs(str(paths.BUG_REPORT_DIR), exist_ok=True)
351
+ paths.BUG_REPORT_DIR.mkdir(exist_ok=True, parents=True)
88
352
 
89
353
  print(r'Invoking poxy')
90
354
  result = subprocess.run(
91
- args=[r'poxy', *bug_report_args, r'--bug-report-internal', r'--verbose'],
355
+ args=[
356
+ r'poxy',
357
+ r'--worker',
358
+ r'--verbose',
359
+ r'--nocleanup',
360
+ r'--output-dir',
361
+ str(paths.BUG_REPORT_DIR),
362
+ r'--temp-dir',
363
+ str(paths.BUG_REPORT_DIR / r'temp'),
364
+ r'--copy-config-to',
365
+ str(paths.BUG_REPORT_DIR),
366
+ *bug_report_args,
367
+ ],
92
368
  cwd=str(Path.cwd()),
93
- check=False,
94
- stdout=subprocess.PIPE,
95
- stderr=subprocess.STDOUT,
369
+ capture_output=True,
96
370
  encoding='utf-8',
97
371
  )
98
372
 
99
- if result.stdout is not None:
373
+ if result.stdout:
100
374
  print(r'Writing stdout')
101
375
  with open(paths.BUG_REPORT_DIR / r'stdout.txt', r'w', newline='\n', encoding=r'utf-8') as f:
102
376
  f.write(result.stdout)
103
377
 
104
- if result.stderr is not None:
378
+ if result.stderr:
105
379
  print(r'Writing stderr')
106
380
  with open(paths.BUG_REPORT_DIR / r'stderr.txt', r'w', newline='\n', encoding=r'utf-8') as f:
107
381
  f.write(result.stderr)
@@ -130,8 +404,8 @@ def bug_report():
130
404
  delete_directory(paths.BUG_REPORT_DIR)
131
405
 
132
406
  print(
133
- f'Zip generated: {bug_report_zip}\n'
134
- 'Please attach this file when you make a report at github.com/marzer/poxy/issues, thanks!'
407
+ f'Zip generated: {Style.BRIGHT}{bug_report_zip}{Style.RESET_ALL}\n'
408
+ rf'{Fore.CYAN}{Style.BRIGHT}Please attach this file when you make a report at https://github.com/marzer/poxy/issues, thanks!{Style.RESET_ALL}'
135
409
  )
136
410
 
137
411
 
@@ -146,12 +420,13 @@ def main(invoker=True):
146
420
  # yapf: disable
147
421
  args = argparse.ArgumentParser(
148
422
  description=
423
+ rf'{Fore.CYAN}{Style.BRIGHT}'
149
424
  r''' _ __ _____ ___ _ ''' '\n'
150
425
  r''' | '_ \ / _ \ \/ / | | |''' '\n'
151
426
  r''' | |_) | (_) > <| |_| |''' '\n'
152
427
  r''' | .__/ \___/_/\_\\__, |''' '\n'
153
428
  r''' | | __/ |''' '\n'
154
- r''' |_| |___/ ''' rf' v{VERSION_STRING} - github.com/marzer/poxy'
429
+ r''' |_| |___/ ''' rf'{Style.RESET_ALL} v{VERSION_STRING} - github.com/marzer/poxy'
155
430
  '\n\n'
156
431
  r'Generate fancy C++ documentation.',
157
432
  formatter_class=argparse.RawTextHelpFormatter
@@ -166,8 +441,8 @@ def main(invoker=True):
166
441
  r'config',
167
442
  type=Path,
168
443
  nargs='?',
169
- default=Path('.'),
170
- help=r'path to poxy.toml or a directory containing it (default: %(default)s)',
444
+ default=Path.cwd(),
445
+ help=r'path to poxy.toml or a directory containing it (default: .)',
171
446
  )
172
447
  args.add_argument(r'-v', r'--verbose', action=r'store_true', help=r"enable very noisy diagnostic output") #
173
448
  make_boolean_optional_arg(args, r'html', default=True, help=r'specify whether HTML output is required') #
@@ -183,13 +458,13 @@ def main(invoker=True):
183
458
  type=str,
184
459
  default=None,
185
460
  metavar=r'<regex>',
186
- help=r"pattern matching HTML file names to exclude from post-processing (default: none)",
461
+ help=r"pattern matching HTML file names to exclude from post-processing (default: %(default)s)",
187
462
  )
188
463
  args.add_argument(
189
464
  r'--theme', #
190
465
  choices=[r'light', r'dark', r'custom'],
191
466
  default=None,
192
- help=r'override the default visual theme (default: read from config)',
467
+ help=r'sets the default visual theme (default: read from config)',
193
468
  )
194
469
  args.add_argument(
195
470
  r'--threads',
@@ -200,14 +475,27 @@ def main(invoker=True):
200
475
  )
201
476
  args.add_argument(r'--version', action=r'store_true', help=r"print the version and exit", dest=r'print_version') #
202
477
  make_boolean_optional_arg(args, r'xml', default=False, help=r'specify whether XML output is required') #
478
+ make_boolean_optional_arg(
479
+ args, r'werror', default=None, help=r'treat warnings as errors (default: read from config)'
480
+ ) #
481
+ args.add_argument(
482
+ r'--bug-report', action=r'store_true', help=r"captures all output in a zip file for easier bug reporting." #
483
+ )
484
+ args.add_argument(
485
+ r'--git-tags', action=r'store_true', help=r"add git-tag-based semver version switcher to the generated HTML" #
486
+ )
203
487
  make_boolean_optional_arg(
204
488
  args,
205
- r'werror',
206
- default=None,
207
- help=r'override the treating of warnings as errors (default: read from config)', #
489
+ r'squash-patches',
490
+ default=True,
491
+ help='when using --git-tags and two version tags differ by a patch number,\ngenerate docs for the highest one only (default: %(default)s)',
208
492
  )
209
493
  args.add_argument(
210
- r'--bug-report', action=r'store_true', help=r"captures all output in a zip file for easier bug reporting." #
494
+ r'--min-version',
495
+ type=str,
496
+ default=None,
497
+ metavar='<version>',
498
+ help='sets the minimum version number to emit when using --git-tags,\nor a negative integer to mean "the last N versions". (default: %(default)s)',
211
499
  )
212
500
 
213
501
  # --------------------------------------------------------------
@@ -222,14 +510,16 @@ def main(invoker=True):
222
510
  args.add_argument(r'--update-emoji', action=r'store_true', help=argparse.SUPPRESS) #
223
511
  args.add_argument(r'--update-tests', action=r'store_true', help=argparse.SUPPRESS) #
224
512
  args.add_argument(r'--doxygen-version', action=r'store_true', help=argparse.SUPPRESS) #
225
- args.add_argument(
226
- r'--update-mcss', type=Path, default=None, metavar=r'<path>', help=argparse.SUPPRESS, dest=r'mcss'
227
- ) #
513
+ args.add_argument(r'--update-mcss', type=Path, default=None, help=argparse.SUPPRESS) #
228
514
  args.add_argument( # --xml and --html are the replacements for --xmlonly
229
515
  r'--xmlonly', action=r'store_true', help=argparse.SUPPRESS #
230
516
  )
231
517
  args.add_argument(r'--xml-v2', action=r'store_true', help=argparse.SUPPRESS) #
232
- args.add_argument(r'--bug-report-internal', action=r'store_true', help=argparse.SUPPRESS) #
518
+ args.add_argument(r'--worker', action=r'store_true', help=argparse.SUPPRESS) #
519
+ args.add_argument(r'--output-dir', type=Path, default=Path.cwd(), help=argparse.SUPPRESS) #
520
+ args.add_argument(r'--temp-dir', type=Path, default=None, help=argparse.SUPPRESS) #
521
+ args.add_argument(r'--copy-config-to', type=Path, default=None, help=argparse.SUPPRESS) #
522
+ args.add_argument(r'--versions-in-navbar', action=r'store_true', help=argparse.SUPPRESS) #
233
523
  args = args.parse_args()
234
524
 
235
525
  # --------------------------------------------------------------
@@ -260,11 +550,11 @@ def main(invoker=True):
260
550
  # developer-only subcommands
261
551
  # --------------------------------------------------------------
262
552
 
263
- if args.mcss is not None:
553
+ if args.update_mcss is not None:
264
554
  args.update_styles = True
265
555
  if args.update_fonts is None:
266
556
  args.update_fonts = True
267
- mcss.update_bundled_install(args.mcss)
557
+ mcss.update_bundled_install(args.update_mcss)
268
558
  assert_existing_directory(paths.MCSS)
269
559
  assert_existing_file(Path(paths.MCSS, r'documentation/doxygen.py'))
270
560
 
@@ -286,15 +576,33 @@ def main(invoker=True):
286
576
  *[a for a in (r'--verbose' if args.verbose else None, r'--nocleanup' if args.nocleanup else None) if a],
287
577
  )
288
578
 
289
- if args.update_styles or args.update_fonts or args.update_emoji or args.update_tests or args.mcss is not None:
579
+ if (
580
+ args.update_styles
581
+ or args.update_fonts
582
+ or args.update_emoji
583
+ or args.update_tests
584
+ or args.update_mcss is not None
585
+ ):
290
586
  return
291
587
 
588
+ if not args.worker:
589
+ print(rf'{Fore.CYAN}{Style.BRIGHT}poxy{Style.RESET_ALL} v{VERSION_STRING}')
590
+
292
591
  # --------------------------------------------------------------
293
592
  # bug report invocation
294
593
  # --------------------------------------------------------------
295
594
 
296
595
  if args.bug_report:
297
- bug_report()
596
+ bug_report(args)
597
+ return
598
+
599
+ # --------------------------------------------------------------
600
+ # multi-version invocation
601
+ # --------------------------------------------------------------
602
+
603
+ if args.git_tags:
604
+ with ScopeTimer(r'All tasks', print_start=False, print_end=True) as timer:
605
+ multi_version_git_tags(args)
298
606
  return
299
607
 
300
608
  # --------------------------------------------------------------
@@ -305,18 +613,11 @@ def main(invoker=True):
305
613
  args.html = False
306
614
  args.xml = True
307
615
 
308
- output_dir = Path.cwd()
309
- temp_dir = None
310
- if args.bug_report_internal:
311
- output_dir = paths.BUG_REPORT_DIR
312
- temp_dir = paths.BUG_REPORT_DIR / r'temp'
313
- args.nocleanup = True
314
-
315
- with ScopeTimer(r'All tasks', print_start=False, print_end=True) as timer:
616
+ with ScopeTimer(r'All tasks', print_start=False, print_end=not args.worker) as timer:
316
617
  run(
317
618
  # named args:
318
619
  config_path=args.config,
319
- output_dir=output_dir,
620
+ output_dir=args.output_dir,
320
621
  output_html=args.html,
321
622
  output_xml=args.xml,
322
623
  threads=args.threads,
@@ -328,8 +629,9 @@ def main(invoker=True):
328
629
  treat_warnings_as_errors=args.werror,
329
630
  theme=args.theme,
330
631
  copy_assets=not args.noassets,
331
- temp_dir=temp_dir,
332
- bug_report=bool(args.bug_report_internal),
632
+ temp_dir=args.temp_dir,
633
+ copy_config_to=args.copy_config_to,
634
+ versions_in_navbar=args.versions_in_navbar,
333
635
  # kwargs:
334
636
  xml_v2=args.xml_v2,
335
637
  )
@@ -355,6 +657,8 @@ def main_blog_post(invoker=True):
355
657
  print(VERSION_STRING)
356
658
  return
357
659
 
660
+ print(rf'{Fore.CYAN}{Style.BRIGHT}poxy{Style.RESET_ALL} v{VERSION_STRING}')
661
+
358
662
  date = datetime.datetime.now().date()
359
663
 
360
664
  title = args.title.strip()
poxy/paths.py CHANGED
@@ -46,3 +46,6 @@ TEMP = Path(tempfile.gettempdir(), r'poxy')
46
46
 
47
47
  BUG_REPORT_DIR = (TEMP / r'bug_report').resolve()
48
48
  """Directory used for assembling bug reports."""
49
+
50
+ CASE_SENSITIVE = not (Path(str(PACKAGE).upper()).exists() and Path(str(PACKAGE).lower()).exists())
51
+ """True if the file system is case-sensitive."""
poxy/project.py CHANGED
@@ -20,6 +20,7 @@ except ImportError:
20
20
 
21
21
  import datetime
22
22
  import itertools
23
+ from colorama import Fore, Style
23
24
 
24
25
  from . import doxygen, emoji, paths, repos
25
26
  from .schemas import *
@@ -1119,7 +1120,7 @@ class Context(object):
1119
1120
  if self.warnings.treat_as_errors:
1120
1121
  raise WarningTreatedAsError(msg)
1121
1122
  else:
1122
- self.__log(logging.WARNING, rf'Warning: {msg}', indent=indent)
1123
+ self.__log(logging.WARNING, rf'{Style.BRIGHT}{Fore.YELLOW}warning:{Style.RESET_ALL} {msg}', indent=indent)
1123
1124
 
1124
1125
  def verbose_value(self, name, val):
1125
1126
  if not self.__verbose:
@@ -1175,7 +1176,8 @@ class Context(object):
1175
1176
  theme: str,
1176
1177
  copy_assets: bool,
1177
1178
  temp_dir: Path = None,
1178
- bug_report: bool = False,
1179
+ copy_config_to: Path = None,
1180
+ versions_in_navbar: bool = False,
1179
1181
  **kwargs,
1180
1182
  ):
1181
1183
  self.logger = logger
@@ -1185,9 +1187,7 @@ class Context(object):
1185
1187
  self.cleanup = bool(cleanup)
1186
1188
  self.copy_assets = bool(copy_assets)
1187
1189
  self.verbose_logger = logger if self.__verbose else None
1188
- self.bug_report = bool(bug_report)
1189
-
1190
- self.info(rf'Poxy v{VERSION_STRING}')
1190
+ self.versions_in_navbar = bool(versions_in_navbar)
1191
1191
 
1192
1192
  self.verbose_value(r'Context.output_html', self.output_html)
1193
1193
  self.verbose_value(r'Context.output_xml', self.output_xml)
@@ -1242,21 +1242,17 @@ class Context(object):
1242
1242
  self.output_dir = coerce_path(output_dir).resolve()
1243
1243
  self.verbose_value(r'Context.output_dir', self.output_dir)
1244
1244
  assert self.output_dir.is_absolute()
1245
- self.case_sensitive_paths = not (
1246
- Path(str(paths.PACKAGE).upper()).exists() and Path(str(paths.PACKAGE).lower()).exists()
1247
- )
1248
- self.verbose_value(r'Context.case_sensitive_paths', self.case_sensitive_paths)
1249
1245
 
1250
1246
  # config path
1251
1247
  self.config_path = Path(r'poxy.toml').resolve()
1252
1248
  if config_path is not None:
1253
1249
  self.config_path = coerce_path(config_path).resolve()
1254
- if self.config_path.exists() and self.config_path.is_dir():
1250
+ if self.config_path.is_dir():
1255
1251
  self.config_path = Path(self.config_path, r'poxy.toml')
1256
- if not self.config_path.exists() or not self.config_path.is_file():
1252
+ if not self.config_path.is_file():
1257
1253
  raise Error(rf"Config '{self.config_path}' did not exist or was not a file")
1258
- if self.bug_report:
1259
- copy_file(self.config_path, paths.BUG_REPORT_DIR)
1254
+ if copy_config_to is not None and self.config_path.is_file():
1255
+ copy_file(self.config_path, copy_config_to)
1260
1256
  assert self.config_path.is_absolute()
1261
1257
  self.verbose_value(r'Context.config_path', self.config_path)
1262
1258
 
@@ -1270,13 +1266,7 @@ class Context(object):
1270
1266
  if temp_dir is not None:
1271
1267
  self.temp_dir = Path(temp_dir).absolute()
1272
1268
  else:
1273
- self.temp_dir = re.sub(r'''[!@#$%^&*()+={}<>;:'"_\\/\n\t -]+''', r'_', str(self.input_dir).strip(r'\/'))
1274
- if len(self.temp_dir) > 256:
1275
- self.temp_dir = str(self.input_dir)
1276
- if not self.case_sensitive_paths:
1277
- self.temp_dir = self.temp_dir.upper()
1278
- self.temp_dir = sha1(self.temp_dir)
1279
- self.temp_dir = Path(paths.TEMP, self.temp_dir)
1269
+ self.temp_dir = paths.TEMP / 'contexts' / temp_dir_name_for(self.input_dir)
1280
1270
  self.verbose_value(r'Context.temp_dir', self.temp_dir)
1281
1271
  assert self.temp_dir.is_absolute()
1282
1272
 
@@ -1716,6 +1706,12 @@ class Context(object):
1716
1706
  elif self.navbar[i] in (r'sponsorship', r'funding', r'fund'):
1717
1707
  self.navbar[i] = r'sponsor'
1718
1708
 
1709
+ # version switcher
1710
+ if self.versions_in_navbar and r'version' not in self.navbar:
1711
+ self.navbar.append(r'version')
1712
+ while not self.versions_in_navbar and r'version' in self.navbar:
1713
+ self.navbar.remove(r'version')
1714
+
1719
1715
  # twitter
1720
1716
  if self.twitter and r'twitter' not in self.navbar:
1721
1717
  self.navbar.append(r'twitter')
poxy/run.py CHANGED
@@ -1289,7 +1289,7 @@ def preprocess_mcss_config(context: Context):
1289
1289
  if not found:
1290
1290
  bar[i] = None
1291
1291
  bar = [b for b in bar if b is not None]
1292
- # handle theme, repo, sponsor, twitter
1292
+ # handle theme, repo, sponsor, twitter, version
1293
1293
  for i in range(len(bar)):
1294
1294
  bar[i] = bar[i].strip()
1295
1295
  if bar[i] == r'repo':
@@ -1343,6 +1343,8 @@ def preprocess_mcss_config(context: Context):
1343
1343
  + rf'class="poxy-icon sponsor">{svg}</a>',
1344
1344
  [],
1345
1345
  )
1346
+ elif bar[i] == r'version':
1347
+ bar[i] = (rf'<span class="poxy-navbar-version-selector">FIXME</span>', [])
1346
1348
  elif bar[i] in context.compounds:
1347
1349
  bar[i] = (
1348
1350
  rf'<a href="{context.compounds[bar[i]]["filename"]}">{context.compounds[bar[i]]["title"]}</a>',
@@ -1684,7 +1686,8 @@ def run(
1684
1686
  theme: str = None,
1685
1687
  copy_assets: bool = True,
1686
1688
  temp_dir: Path = None,
1687
- bug_report: bool = False,
1689
+ copy_config_to: Path = None,
1690
+ versions_in_navbar: bool = False,
1688
1691
  **kwargs,
1689
1692
  ):
1690
1693
  timer = lambda desc: ScopeTimer(desc, print_start=True, print_end=context.verbose_logger)
@@ -1704,7 +1707,8 @@ def run(
1704
1707
  theme=theme,
1705
1708
  copy_assets=copy_assets,
1706
1709
  temp_dir=temp_dir,
1707
- bug_report=bug_report,
1710
+ copy_config_to=copy_config_to,
1711
+ versions_in_navbar=versions_in_navbar,
1708
1712
  **kwargs,
1709
1713
  ) as context:
1710
1714
  preprocess_doxyfile(context)
poxy/utils.py CHANGED
@@ -11,14 +11,14 @@ import io
11
11
  import logging
12
12
  import re
13
13
  import sys
14
- import typing
15
- from pathlib import Path
14
+ import typing # used transitively
15
+ from pathlib import Path # used transitively
16
16
 
17
17
  import requests
18
18
  from misk import *
19
19
  from trieregex import TrieRegEx
20
20
 
21
- from . import paths
21
+ from . import paths # used transitively
22
22
 
23
23
  # =======================================================================================================================
24
24
  # FUNCTIONS
@@ -115,6 +115,16 @@ def remove_duplicates(vals: list) -> list:
115
115
  return new_vals
116
116
 
117
117
 
118
+ def temp_dir_name_for(input, path=None):
119
+ out = re.sub(r'''[!@#$%^&*()+={}<>;:'"_\\/\n\t -]+''', r'_', str(input).strip(r'\/'))
120
+ if len(out) > 256:
121
+ out = str(input)
122
+ if not paths.CASE_SENSITIVE:
123
+ out = out.upper()
124
+ out = sha1(out)
125
+ return out
126
+
127
+
118
128
  # =======================================================================================================================
119
129
  # REGEX REPLACER
120
130
  # =======================================================================================================================
poxy/version.txt CHANGED
@@ -1 +1 @@
1
- 0.15.0
1
+ 0.17.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: poxy
3
- Version: 0.15.0
3
+ Version: 0.17.0
4
4
  Summary: Documentation generator for C++.
5
5
  Author-email: Mark Gillard <mark.gillard@outlook.com.au>
6
6
  License: MIT
@@ -28,6 +28,7 @@ Requires-Dist: tomli
28
28
  Requires-Dist: schema !=0.7.5
29
29
  Requires-Dist: requests
30
30
  Requires-Dist: trieregex
31
+ Requires-Dist: colorama
31
32
 
32
33
  # poxy
33
34
 
@@ -100,7 +101,8 @@ Poxy is a command-line application.
100
101
  ```
101
102
  usage: poxy [-h] [-v] [--html | --no-html] [--ppinclude <regex>] [--ppexclude <regex>]
102
103
  [--theme {light,dark,custom}] [--threads N] [--version] [--xml | --no-xml]
103
- [--werror | --no-werror] [--bug-report]
104
+ [--werror | --no-werror] [--bug-report] [--git-tags]
105
+ [--squash-patches | --no-squash-patches] [--min-version <version>]
104
106
  [config]
105
107
 
106
108
  _ __ _____ ___ _
@@ -108,7 +110,7 @@ usage: poxy [-h] [-v] [--html | --no-html] [--ppinclude <regex>] [--ppexclude <r
108
110
  | |_) | (_) > <| |_| |
109
111
  | .__/ \___/_/\_\\__, |
110
112
  | | __/ |
111
- |_| |___/ v0.12.0 - github.com/marzer/poxy
113
+ |_| |___/ v0.17.0 - github.com/marzer/poxy
112
114
 
113
115
  Generate fancy C++ documentation.
114
116
 
@@ -118,17 +120,24 @@ positional arguments:
118
120
  options:
119
121
  -h, --help show this help message and exit
120
122
  -v, --verbose enable very noisy diagnostic output
121
- --html, --no-html specify whether HTML output is required (default: True)
123
+ --html, --no-html specify whether HTML output is required
122
124
  --ppinclude <regex> pattern matching HTML file names to post-process (default: all)
123
- --ppexclude <regex> pattern matching HTML file names to exclude from post-processing (default: none)
125
+ --ppexclude <regex> pattern matching HTML file names to exclude from post-processing (default: None)
124
126
  --theme {light,dark,custom}
125
- override the default visual theme (default: read from config)
127
+ sets the default visual theme (default: read from config)
126
128
  --threads N set the number of threads to use (default: automatic)
127
129
  --version print the version and exit
128
- --xml, --no-xml specify whether XML output is required (default: False)
130
+ --xml, --no-xml specify whether XML output is required
129
131
  --werror, --no-werror
130
- override the treating of warnings as errors (default: read from config)
132
+ treat warnings as errors (default: read from config)
131
133
  --bug-report captures all output in a zip file for easier bug reporting.
134
+ --git-tags add git-tag-based semver version switcher to the generated HTML
135
+ --squash-patches, --no-squash-patches
136
+ when using --git-tags and two version tags differ by a patch number,
137
+ generate docs for the highest one only (default: True)
138
+ --min-version <version>
139
+ sets the minimum version number to emit when using --git-tags,
140
+ or a negative integer to mean "the last N versions". (default: None)
132
141
  ```
133
142
 
134
143
  The basic three-step to using Poxy is similar to Doxygen:
@@ -212,6 +221,15 @@ Poxy bundles a fork of m.css, used per the [MIT/Expat license](https://github.co
212
221
 
213
222
  # Changelog
214
223
 
224
+ ## v0.17.0 - 2024-04-21
225
+
226
+ - added arguments `--min-version` and `--squash-patches` for more control over `--git-tags` mode
227
+
228
+ ## v0.16.0 - 2024-01-28
229
+
230
+ - added multi-version mode argument `--git-tags`
231
+ - added colour to output
232
+
215
233
  ## v0.15.0 - 2023-12-08
216
234
 
217
235
  - added config option `main_page` (a.k.a. `USE_MDFILE_AS_MAINPAGE`)
@@ -5,30 +5,30 @@ poxy/doxygen.py,sha256=nHygTYfKqh4M0hcdomWK5J3T16GZ7owM5oaWa141Ky4,46372
5
5
  poxy/emoji.py,sha256=Vj2ZbUq1MewLMVoLtH2xP_jZToRoNi52AgrIg3VhUuU,3410
6
6
  poxy/fixers.py,sha256=aUIQu4YEaygDK4YIaQJ9HsbP6uUKR3XYfroJQeps64M,44783
7
7
  poxy/graph.py,sha256=xI7xoV6-yTM-gh2OBARakANLHzGYBwwhwJHchQz2EKw,31959
8
- poxy/main.py,sha256=Eo38pPB02yHB5u6CbRsl1dfJyPyR2xqoYggGrEqN4Vs,14205
8
+ poxy/main.py,sha256=X8IvjjEnxwV0oT2lv5cP24CQjxHKpsxDmfByoPP2gxg,26937
9
9
  poxy/mcss.py,sha256=j9PmkfjcwaKdKBauwiPKhSeKGNmGiBt0i_WcnVAV3Hk,2157
10
- poxy/paths.py,sha256=nLwBy0a0MA1yXO9JqLsmr1kajuo6PvABqz9rjiwNJXE,1391
11
- poxy/project.py,sha256=DhSAr-HdI_FPW4jbD9Ud8_XZNcnz-gCOd7T4aFyd-mw,96645
10
+ poxy/paths.py,sha256=OqyP9bIIhW0zyWL6PV2TVV9zsrtvnb25_XNmHlFJmQ8,1540
11
+ poxy/project.py,sha256=TMb3kiMhlvvu4X4XsdtwrhbF2vWOZoeAKu15aMu3bbY,96377
12
12
  poxy/repos.py,sha256=rUOVTvR6KxrkSvEA0S340OzMStbSk2SBJUEzKEIynzc,3945
13
- poxy/run.py,sha256=UT-65Op-tQkIGExKXEXEecwrpZhL1OVcsvVKGezDSdU,76806
13
+ poxy/run.py,sha256=ze2A5YI26KXFrh-t1wGODgIJQRo5lR9-9eQhvhnloZc,77049
14
14
  poxy/schemas.py,sha256=57qLWubeIWQUwwTpjRBggPBy70PTQS5JWKzUDKRuBkQ,2490
15
15
  poxy/soup.py,sha256=gMXKigNJDuJWWFhPpD5Gu9gbeyVVXo7SrjL1GcsqlMs,7678
16
16
  poxy/svg.py,sha256=tsMcKIxQ8OVmXEabqwKDkdEqYV4zI5wM0AJ-E1EXmVo,2657
17
- poxy/utils.py,sha256=F1Rpb3H7h0GUSuQSljwmgt-V-Nv115VzxTP4MDWH088,5398
17
+ poxy/utils.py,sha256=0_HRU6GxlwJ5TXPJId2j8zQJnUZ8TJBiBU6wqgrMjrI,5748
18
18
  poxy/version.py,sha256=N5I7nr5zcmAy-hhgZFi7KfFPV-lB_ueD_Xx6R7XQ-tk,668
19
- poxy/version.txt,sha256=-EBaczDChwKNRww-41Bvh48a2F7XnB0a3BnfL_c5iRU,7
19
+ poxy/version.txt,sha256=AtOOmOiPSo3C6kDbt2kzhgEGb8lJILUogqFPIa2X9-4,7
20
20
  poxy/xml_utils.py,sha256=dJEtHyp4lORHhmBt1E9miRH48PpPM6WRlLEdMhn0Yi8,2183
21
21
  poxy/css/m-special.css,sha256=dLVigwqsg_Htm6CESQGemyOKjb-plBlFk54dsAoOd_A,4644
22
22
  poxy/css/m-theme-dark.css,sha256=UbAEz9JAgJ1PJJG-Oy9UG2_DUqYTaPJm1cDvVGy5kYk,4186
23
23
  poxy/css/m-theme-light.css,sha256=mpXFxB3Uz8_Zklw1cHuJaUA5rxkGvvlOBA2S3Pl1tCY,8035
24
- poxy/css/poxy-overrides.css,sha256=l8bP4SfEk2DVsKBvIMMZIxLaMLy1M0aR1GjpaE52SYA,10426
24
+ poxy/css/poxy-overrides.css,sha256=3G04c2KR9ZErJo_ivgH1D86XtMP3vPJ4yrLHBvHTrq4,10426
25
25
  poxy/css/poxy-pygments.css,sha256=xuyYAQ6agAzb4WnjK2emHvcmuy4gOJejogerO7GBJTw,8376
26
26
  poxy/css/poxy-theme-dark.css,sha256=-uBMAbHHFsAdmBbNVcX_7waOcs_uiNVBHCRMeKmAtP4,897
27
27
  poxy/css/poxy-theme-light.css,sha256=YB29Y9IBhAZbRRu2RITRRigaRp0RBRg_MsyCV0u8wYo,1363
28
28
  poxy/css/poxy.css,sha256=wf_XA8DNMH7pML9d1v2p73AI6rGCjcr4hQxNlfdT-d8,1373
29
29
  poxy/generated/6aa12ac32e258617e37a0030dc54f789b894368b.css,sha256=2dgvV-yz0S6U1FUH3mPPfVOy7Zt5-RnG7J1bTlXpyLo,16324
30
30
  poxy/generated/emoji.json,sha256=4djkUv00kbpfcXE2ytUk08NxzOg_28tECuDqo8U6bkE,250504
31
- poxy/generated/poxy.css,sha256=5YyhhQ-jd8PRxIlgS77OtX3x-J6uue8kL_pTiOfa_9g,144548
31
+ poxy/generated/poxy.css,sha256=yGs-703hBQ1WMUwnfpiruvj3sykgz5vXF-wC0PDH624,144548
32
32
  poxy/generated/fonts/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7jsDJT9g.woff2,sha256=aKqs2fm9oq7U6gUieWuM5I7fvSrdqeREtzFQtbd4Fmk,1036
33
33
  poxy/generated/fonts/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7ksDJT9g.woff2,sha256=E9YKtNAGDp7xjSo4a84V9pBTGC2pz6taS6rEgV8eqTY,1212
34
34
  poxy/generated/fonts/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7nsDI.woff2,sha256=UA-Kr2ndz3GhbOrljJJ_AzcbM2ZRheFt80e2f38Rvbk,14160
@@ -176,10 +176,10 @@ poxy/mcss/plugins/m/plots.py,sha256=tiPvTZDkJ-Ab7xSVUhi5CFf_KvO017ZEtxS-ZQYJPiM,
176
176
  poxy/mcss/plugins/m/qr.py,sha256=EpYTecbB0m6IzJMDNpIeMjh3-bxisGw6sCdQgL3_Zl4,4183
177
177
  poxy/mcss/plugins/m/sphinx.py,sha256=OZHtZ0qVHYHGxJVpSARk_WQiIr6o9B-CTqx2Limfyuw,30965
178
178
  poxy/mcss/plugins/m/vk.py,sha256=MiZeFEagOkMBHfNXYf1YEaooCbgoGBtFY9K16CDqdaw,3108
179
- poxy-0.15.0.dist-info/LICENSE.txt,sha256=kp84JW_RPClTO5Hz6yHQ9jKPfUMerlHHAeyU0VpXV_E,1064
180
- poxy-0.15.0.dist-info/METADATA,sha256=4ORW3fkvgEIUAJdxJVq_fLYW5ugYnJCqgzbFA2Ug7TI,18887
181
- poxy-0.15.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
182
- poxy-0.15.0.dist-info/entry_points.txt,sha256=yWiOmptj1Ga_dDEU9ch16hRgmzDE8bgrtDkGbH7VFYc,66
183
- poxy-0.15.0.dist-info/top_level.txt,sha256=vAxxbZDX_cQ6rfRZ1SYSStSdMMlsHEP-zgzBSB0gPco,5
184
- poxy-0.15.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
185
- poxy-0.15.0.dist-info/RECORD,,
179
+ poxy-0.17.0.dist-info/LICENSE.txt,sha256=kp84JW_RPClTO5Hz6yHQ9jKPfUMerlHHAeyU0VpXV_E,1064
180
+ poxy-0.17.0.dist-info/METADATA,sha256=DrK1i-r3OyMIsuuS1rCXA40UxijYv0MV466O0IBf5wA,19700
181
+ poxy-0.17.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
182
+ poxy-0.17.0.dist-info/entry_points.txt,sha256=yWiOmptj1Ga_dDEU9ch16hRgmzDE8bgrtDkGbH7VFYc,66
183
+ poxy-0.17.0.dist-info/top_level.txt,sha256=vAxxbZDX_cQ6rfRZ1SYSStSdMMlsHEP-zgzBSB0gPco,5
184
+ poxy-0.17.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
185
+ poxy-0.17.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5