Package setuplib ::
Module setuplib
1
2 """Setuplib subcommand 'ENUMERATE'.
3 Enumerate available commands based on 'pkt_resources'.
4 Supports parameters and filters.
5 """
6 from __future__ import absolute_import
7 from __future__ import print_function
8
9 import sys
10 import os
11 import re
12 import traceback
13
14 import distutils.cmd
15
16 import pkg_resources
17
18 import yapydata.datatree.datatree as datatree
19 import setuplib
20
21 __author__ = 'Arno-Can Uestuensoez'
22 __author_email__ = 'acue_sf2@sourceforge.net'
23 __license__ = "Artistic-License-2.0 + Forced-Fairplay-Constraints"
24 __copyright__ = "Copyright (C) 2015-2019 Arno-Can Uestuensoez @Ingenieurbuero Arno-Can Uestuensoez"
25 __uuid__ = "239b0bf7-674a-4f53-a646-119f591af806"
26
27 __version__ = "01.01.005"
28
29 __product_family__ = "setuplib"
30 __product__ = "setuplib"
31 __product_component__ = "list_enty_points"
32
33
35 """Error on setuplib calls.
36 """
37 pass
38
39
40 -class SetupListEntryPointsX(distutils.cmd.Command):
41 """List available entry points."""
42
43 description = 'List available entry points.'
44 user_options = [
45 ('debug', 'd', "Raises degree of debug traces of current context. Supports repetition. "
46 "Each raises the debug verbosity level of the context by one."),
47 ('exit', 'e', "Exit after command 'setuplib' immediately, ignore following. "
48 "Default := off."),
49 ('filter=', None, "Define a filter, for details refer to the manual. "
50 "Default: ''"),
51 ('format=', 'f', "Define display format. "
52 "See '--format=help. "
53 "Default: 'name,module_name,dist.egg_info:::fname'"),
54 ('group=', 'g', "Set group for scan, '--group=none' scans all, "
55 "'--group=console_scripts' displays all console scripts, "
56 "etc. "
57 "For current list: '--group=help', similar to '--list-groups'. "
58 "See 'PyPA.io'."
59 "Default: 'distutils.commands'"),
60 ('ignore-missing', 'i', "Ignore errors due to missing components, and continue. "
61 " For example in case of missing an optional. "
62 "Default: False. "),
63 ('layout=', None, "Define display layout. "
64 "See '--format=help'. "
65 "Default: table"),
66 ('list-groups', None, "Lists all scanned groups. "
67 "Is shortcut for the '--format' option, "
68 "refer to the manuals for additional details. "
69 "Default: names only."
70 ),
71 ('long', 'l', "List long format, similar to shell command 'ls -l'. "
72 "Default: off"),
73 ('quiet', 'q', "Suppress display including warnings. Display error messages only."
74 "Default: off"),
75 ('search-path', 'P', "Set the search path for requested resources. "
76 "Default: 'sys.path'"),
77 ('sort=', None, "Sort a specified field number. "
78 "Default: 0"),
79 ('verbose', 'v', "Raises verbosity of current context. Supports repetition. "
80 "Each raises the command verbosity level of the context by one. "
81 "The value is defined by the global option defined in "
82 "'Distribution'. "
83 "Refer to the manuals for the special behaviour when used as "
84 "either a global option(start 'verbose=1'), "
85 "or as a command context option(start 'verbose=0'). "
86 "Default:=1."),
87 ]
88
89
91 """Define the API entrypoints and data of the command 'list'.
92
93 REMARK: verbose and debug are encapsulated/hidden by distutils.
94
95 """
96 self.alias = None
97 self.at_once = None
98 self.debug = None
99 self.exit = None
100 self.filter = None
101 self.format = None
102 self.group = None
103 self.ignore_missing = None
104 self.layout = None
105 self.long = None
106 self.list_groups = None
107 self.quiet = None
108 self.search_path = None
109 self.sort = None
110 self.verbose = None
111
113 """Initializae the API of 'lis'.
114 """
115
116
117
118 try:
119
120
121
122
123
124
125
126 _v_opts = self.distribution.get_option_dict('setuplib')['verbose'][1]
127 if _v_opts:
128 self.verbose += 1
129 except:
130
131
132 pass
133
134
135 try:
136
137
138 _q_opts = self.distribution.get_option_dict('setuplib')['quiet'][1]
139 if _q_opts:
140 self.quiet += 1
141 except:
142
143
144 if self.quiet == None:
145 self.quiet = 0
146 pass
147
148
149
150 if self.debug == None:
151 self.debug = 0
152
153
154 if self.ignore_missing == None:
155 self.ignore_missing = False
156
157 if self.exit == None:
158 self.exit = False
159
160 if self.long == None:
161 self.long = 0
162
163 if self.sort == None:
164 self.sort = 0
165
166 if self.list_groups != None:
167 self.list_groups = True
168
169 if self.group == None:
170
171 if not self.list_groups:
172 self.group = 'distutils.commands'
173 elif self.group.lower() == 'none':
174 self.group = None
175
176
177 if self.layout == None:
178 self.layout = 'table'
179 elif self.layout == 'help':
180 print()
181 print("Current layouts are: list, table")
182 print("Soon available: csv, ini, json, .properties, xml, yaml")
183 print()
184 sys.exit(0)
185
186 if self.format == None:
187 self.format = 'name,module_name,dist.egg_info:::fname'
188 elif self.format.lower() == 'help':
189 print("format := (list | table | csl | xml | json | yaml)")
190 sys.exit(0)
191
192
193 _format = []
194 for _f in self.format.split(','):
195 _fx = _f.split(':')
196 if len(_fx) == 0:
197 continue
198 elif _fx[0] and len(_fx) == 1:
199 _format.append([_fx[0], 0, 'auto', ''])
200 elif len(_fx) < 5:
201 _rec = [_fx[0], 0, 'auto', '',]
202
203 if _fx[1]:
204 _rec[1] = int(_fx[1])
205
206 if _fx[2]:
207 if _fx[2].lower() not in (
208 'cl', 'cr', 'clip', 'auto',
209 ):
210 raise SetuplibCommandsxError(
211 "parameter error:%s - %s - in: %s" %(
212 str(_fx[2]),
213 str(_f),
214 str(self.format),
215 )
216 )
217
218 _rec[2] = _fx[2].lower()
219
220 if _fx[3]:
221
222 _rec[3] = _fx[3]
223
224 if _rec[1] or int(_rec[1]) == 0:
225
226 _rec[2] = 'auto'
227
228 _format.append(_rec)
229
230 else:
231 raise SetuplibCommandsxError(
232 "parameter error: %s - in: %s" %(
233 str(_f),
234 str(self.format),
235 )
236 )
237 self.format = _format
238
239
240 if self.filter == None:
241 self.filter = None
242
243
244
245
246 self.task = {
247 "alias": self.alias,
248 "debug": self.debug,
249 "exit": self.exit,
250 "format": self.format,
251 "filter": self.filter,
252 "group": self.group,
253 "ignore_missing": self.ignore_missing,
254 "layout": self.layout,
255 "list_groups": self.list_groups,
256 "long": self.long,
257 "quiet": self.quiet,
258 "search_path": self.search_path,
259 "sort": self.sort,
260 "verbose": self.verbose,
261 }
262
263
264 if self.at_once:
265 _m = MyDistributionData(self.distribution, self.task)
266 _m.enumerate(self.task)
267 _m.print()
268 sys.exit(0)
269
271 """Creates documents.
272 Calls the defined and activated wrapper scripts.
273 """
274 _m = MyDistributionData(self.distribution, self.task)
275 _m.enumerate(self.task)
276
277 _m.print()
278
279
280
281
282
283
284
285
286
288
289
290 combinelogic = {
291 'and': 0,
292 'or': 1,
293 'nand': 2,
294 'nor': 3,
295 'xor': 4,
296 }
297
298 - def __init__(self, distribution, task):
299
300 self.distribution = distribution
301
302 self.task = task
303
304
305 self.ep_cache = {}
306
307
308 self.cmd_ep = []
309
310
311 self.cmd_std = []
312
313
314 self.cmd_local = []
315
316
317 self.cmd_ext_setuptools = []
318
319
320 self.cmd_ext_distutils = []
321
322
323 self.cmd_ext_misc = []
324
325
326 self.cmd_setuplib = []
327
329 """Calls *pkg_resources* and caches the results.
330 The cached data is queried later for details required
331 by the extended list and display commands.
332
333 The caching happens here only, which comprises the flat
334 data cache of all entry points and the additional
335 categorized cache of selective sets for later selection
336 filters.
337
338 The centralized preparation of the data for later filtering
339 eases the data handling significantly by moderate use of
340 additional resources.
341
342 Args:
343 self:
344 The current instance of this class.
345
346 Returns:
347 Results in the member variable::
348
349 self.ep_cache
350
351 The content is a *dict* containing the iterated
352 entry points with the *<ep>.name* as key.
353
354 Raises:
355 pass-through
356
357 """
358 _task = task
359 if not _task:
360 _task = self._task
361
362 if (
363 _task['group'] == None
364 or _task['group'] in ('help',)
365 ):
366 self.ep_cache = list(pkg_resources.iter_entry_points(None))
367 self.ep_cache = [list(x.values())[0] for x in self.ep_cache]
368
369
370
371
372 if _task['group'] in ('help',):
373 _groups = []
374 for e in self.ep_cache:
375 _groups.extend(e.dist.get_entry_map().keys())
376
377 print()
378 print("Current groups:")
379 print()
380 for g in sorted(set(_groups)):
381 print(" " + str(g))
382 print()
383
384 sys.exit(0)
385
386 else:
387 self.ep_cache = list(pkg_resources.iter_entry_points(_task['group']))
388
389 if _task['filter']:
390 _filters = _task['filter'].split(';')
391 _combine = 0
392
393 _query_filters = []
394 for _fx in _filters:
395 if _fx.lower() in 'and':
396 _combine = 0
397 continue
398 elif _fx.lower() == 'or':
399 _combine = 1
400 continue
401 elif _fx.lower() == 'nand':
402 _combine = 2
403 continue
404 elif _fx.lower() == 'nor':
405 _combine = 3
406 continue
407 elif _fx.lower() == 'xor':
408 _combine = 4
409 continue
410
411
412
413
414 _record_filter = _fx.split(":")
415 if len(_record_filter) > 1:
416
417 _query_filters.append(
418 (_record_filter[0].split('.'), re.compile(_record_filter[1]))
419 )
420 elif len(_record_filter) == 1:
421
422 _query_filters.append((_record_filter[0].split('.'), ''))
423
424 if _task['debug'] > 0:
425 print("DBG:input entry points: " + str(len(self.ep_cache)))
426
427 _all_filters = len(_query_filters)
428 for epi in reversed(range(len(self.ep_cache))):
429 _match_cnt = 0
430 for _fpath,_fexpr in _query_filters:
431
432 try:
433 _v = datatree.DataTree(self.ep_cache[epi])(*_fpath, pysyn=True)
434 if not _fexpr:
435 _match_cnt += 1
436 elif _fexpr.search(str(_v)):
437 _match_cnt += 1
438
439 except datatree.YapyDataDataTreeOidError:
440 if _task['ignore_missing']:
441 break
442 else:
443
444 pass
445
446 else:
447 if not (
448 (_combine == 0 and _match_cnt == _all_filters)
449 or (_combine == 1 and _match_cnt > 0)
450 or (_combine == 2 and _match_cnt < _all_filters)
451 or (_combine == 3 and _match_cnt == 0)
452 or (_combine == 4 and _match_cnt == 1)
453 ):
454 self.ep_cache.pop(epi)
455
456 if _task['debug'] > 0:
457 print("DBG:filtered entry points: " + str(len(self.ep_cache)))
458
459 self.ep_index = {}
460 idx = 0
461 for e in self.ep_cache:
462 self.ep_index[e.name] = idx
463 idx += 1
464
465 return
466
468
469 """Print '--list-groups' output based on pre-filtered data.
470 Thus the complete set of filters processed in enumerate
471 are applicable.
472 """
473 _t = self.task
474 self.ep_cache = list(pkg_resources.iter_entry_points(self.task['group']))
475 if self.task['group'] == None:
476 self.ep_cache = [list(x.values())[0] for x in self.ep_cache]
477
478 _groups = {}
479 for e in self.ep_cache:
480 _gmap = e.dist.get_entry_map()
481 for k,v in _gmap.items():
482 if not _groups.get(k):
483 _groups[k] = v
484 else:
485 _groups[k].update(v)
486
487 pass
488
489
490
491
492
493
494 if _t['layout'] == 'list':
495
496 self.print_groups_list(_groups, index, self.task)
497 elif _t['layout'] == 'xml':
498 raise NotImplementedError("XML is not yet available")
499 elif _t['layout'] == 'json':
500 raise NotImplementedError("JSON is not yet available")
501 elif _t['layout'] == 'yaml':
502 raise NotImplementedError("yaml is not yet available")
503 elif _t['layout'] == 'csv':
504 raise NotImplementedError("csv is not yet available")
505 elif _t['layout'] == 'table':
506 self.print_groups_list(_groups, index, self.task)
507
508 else:
509 raise SetuplibCommandsxError(
510 "Unknown layout: " + str(_t['layout'])
511 )
512
513
515 """Print '--list-groups' output based on pre-filtered data.
516 Thus the complete set of filters are applicable.
517 """
518
519 _adjust_level = self.task['long']
520
521
522 print()
523 print("Current groups:")
524 print()
525 _maxsize = 0
526
527 if _adjust_level == 3:
528 for k,v in sorted(outlist.items()):
529 if self.task['long']:
530 for k1,v1 in sorted(v.items()):
531 if len(k1) > _maxsize:
532 _maxsize = len(k1)
533
534 for k,v in sorted(outlist.items()):
535 print(" " + str(k))
536 if self.task['long']:
537 if _adjust_level == 2:
538 _maxsize = 0
539 for k1,v1 in sorted(v.items()):
540 if len(k1) > _maxsize:
541 _maxsize = len(k1)
542
543 for k1,v1 in sorted(v.items()):
544
545
546 _v1 = re.sub(k1 + ' *= *', '', str(v1))
547 if _maxsize > 0:
548 _format = " {0:"+ str(_maxsize) + "} = {1}"
549 else:
550 _format = " {0} = {1}"
551 print( _format.format(
552 str(k1),
553 str(_v1),
554 )
555 )
556 print()
557
558 print()
559 sys.exit(0)
560
562 """Prints the requested data.
563
564 The printout is again processed in two levels.
565 - print:
566
567 Prepares the record data for the appropriate format.
568
569 Calls the inteface::
570
571 self.print_<format>(outlist, index, task)
572
573 outlist := The list of resulting keys within the *self.ep_cache*.
574
575 index := The sprocessed/sorted list of *(<key>, #index)* mapping
576 of utlist to *self.ep_cache*.
577
578 task := The parameters of the current task.
579
580 For example::
581
582 self.print_table(outlist, index, task)
583
584 - print_<format>:
585 Prints out the records of the selcted output format.
586
587 Uses object data from *self*.
588
589 """
590
591 _t = self.task
592
593 print()
594 if _t['group'] == None:
595 print("entry points of resource group: None == all")
596 else:
597 print("entry points of resource group: " + str(_t['group']))
598
599 if _t['filter']:
600 print("applied filter: " + str(_t['filter']))
601
602 print()
603
604 if _t['sort'] == 0:
605 _index = self.ep_index
606 _list = sorted(_index)
607
608 elif _t['sort'] == 1:
609 _index = {}
610 idx = 0
611 _fname = {}
612 for e in self.ep_cache:
613 _fname[e.module_name] = (e.name, idx,)
614 idx += 1
615 for x in sorted(_fname.keys()):
616 _index[_fname[x][0]] = _fname[x][1]
617 _list = _index
618
619 elif _t['sort'] != 0:
620
621 _n = _t['sort']
622 _nx = _n.split('.')
623
624 _index = {}
625 idx = 0
626 _fname = {}
627 _grp = _t['group'] == None
628
629 for _e in self.ep_cache:
630 _val = datatree.DataTree(_e)(*_nx, pysyn=True)
631 try:
632 _item = _fname[_val]
633 _item.append((_e.name, idx,))
634 except KeyError:
635 _fname[_val] = [(_e.name, idx,),]
636
637 idx += 1
638
639 for x in sorted(_fname.keys()):
640 for xi in _fname[x]:
641 _index[xi[0]] = xi[1]
642 _list = _index
643
644
645
646
647
648
649 if self.task['list_groups']:
650 self.print_groups(_list, _index, _t)
651
652 else:
653 if _t['layout'] == 'list':
654 self.print_list(_list, _index, _t)
655 elif _t['layout'] == 'xml':
656 raise NotImplementedError("XML is not yet available")
657 elif _t['layout'] == 'json':
658 raise NotImplementedError("JSON is not yet available")
659 elif _t['layout'] == 'yaml':
660 raise NotImplementedError("yaml is not yet available")
661 elif _t['layout'] == 'csv':
662 raise NotImplementedError("csv is not yet available")
663 elif _t['layout'] == 'table':
664 self.print_table(_list, _index, _t)
665 else:
666 raise SetuplibCommandsxError(
667 "Unknown layout: " + str(_t['layout'])
668 )
669
671 """Print table.
672 """
673
674
675
676 _header = task['format']
677 _layout = task['layout']
678
679
680 for _e in self.ep_cache:
681 for i in range(len(_header)):
682 if _header[i][2] not in ('auto', ):
683 continue
684
685 _nx = _header[i][0].split('.')
686
687 try:
688 _val = datatree.DataTree(_e)(*_nx, pysyn=True)
689 except datatree.YapyDataDataTreeOidError:
690 _val = ''
691
692 if (
693 _header[i][3] and _header[i][3] == 'fname'
694 and not task['long']
695 ):
696 _val = os.path.basename(_val)
697 elif (
698 _header[i][3] and _header[i][3] == 'abs'
699 ):
700 _val = os.path.abspath(_val)
701
702 if len(str(_val)) >= _header[i][1]:
703 _header[i][1] = len(str(_val)) + 1
704 elif len(_header[i][0]) >= _header[i][1]:
705 _header[i][1] = len(_header[i][0]) + 1
706
707 _head = [x[0] for x in _header]
708 _format = ''
709 _tabsep = ''
710 _l = len(_header)
711 for i in range(_l):
712 if _header[i][1] == 0 or i == _l - 1:
713 _format += "{%d}"%(i)
714 else:
715 _format += "{%d:%d}"%(i, _header[i][1])
716
717 _tabsep += '-' * _header[i][1]
718
719 print(_format.format(*_head))
720 print(_tabsep)
721
722 _ign = task['ignore_missing']
723 for x in outlist:
724 try:
725 _e = self.ep_cache[index[x]]
726 _values = []
727 for _f in range(len(_head)):
728 _nx = _head[_f].split('.')
729 _len = _header[_f][1] - 1
730 _lx = _header[_f][2]
731 try:
732 _v = datatree.DataTree(_e)(*_nx, pysyn=True)
733 _lv = len(str(_v))
734
735 if (
736 _header[_f][3] and _header[_f][3] == 'fname'
737 and not task['long']
738 ):
739 _v = os.path.basename(_v)
740 elif (
741 _header[_f][3] and _header[_f][3] == 'abs'
742 ):
743 _v = os.path.abspath(_v)
744
745
746 if _lv > _len:
747 if _lx == 'cl':
748 _v = str(_v)[-_len:]
749 elif _lx == 'cr':
750 _v = str(_v)[:_len]
751 elif _lx == 'clip':
752
753 pass
754 elif _lx == 'auto':
755 pass
756
757 _values.append(_v)
758
759 except datatree.YapyDataDataTreeOidError as e:
760 if _ign:
761
762 _values.append('')
763 else:
764
765
766 traceback.print_exc()
767 print("\n\nHINT: You have selected a non-present path, use:\n"
768 "\n '--ignore-missing' / '-i' to continue"
769 "\n '--filter' for the pre-selection of a valid set\n"
770 )
771 sys.exit(1)
772
773
774
775
776
777
778
779
780 if _ign:
781 if ( _values and len(_values) > 1 ) or ''.join([str(x) for x in _values]):
782 print(_format.format(*[str(v) for v in _values]))
783 else:
784 continue
785 else:
786 print(_format.format(*[str(v) for v in _values]))
787
788 except AttributeError:
789 raise
790 except KeyError:
791 raise
792
793 print()
794
796 """Print list.
797 """
798
799 _header = task['format']
800 _layout = task['layout']
801
802 _head = [x[0] for x in _header]
803 _format = ''
804 _commonprefix = 0
805 for i in range(len(_header)):
806 if _commonprefix < len(str(_header[i][0])):
807 _commonprefix = len(str(_header[i][0])) + 1
808
809 prewidth = 5
810
811 _format = "{0} {1:%d}: {2}"%(_commonprefix)
812 _idxstr = ' ' * prewidth
813 _prestr = "\n{0:%d}" % (prewidth)
814 _idx = 0
815
816 for x in outlist:
817 try:
818 for _f in range(len(_head)):
819 if _f:
820 _pre = _idxstr
821 else:
822 _pre = _prestr.format(_idx)
823 _idx += 1
824
825 try:
826 _v = datatree.DataTree(self.ep_cache[index[x]])(
827 *_head[_f].split('.'), pysyn=True
828 )
829
830 if (
831 _header[_f][3] and _header[_f][3] == 'fname'
832 and not task['long']
833 ):
834 _v = os.path.basename(_v)
835 elif (
836 _header[_f][3] and _header[_f][3] == 'abs'
837 ):
838 _v = os.path.abspath(_v)
839
840 _x = _format.format(
841 _pre,
842 _head[_f],
843 _v,
844 )
845 except datatree.YapyDataDataTreeOidError:
846 if task['ignore_missing']:
847 _x = _format.format(
848 _pre,
849 _head[_f],
850 ''
851 )
852 else:
853 raise
854
855 print(_x)
856
857 except AttributeError:
858 raise
859 except KeyError:
860 raise
861
862 print()
863